From f6ed0a645a8f06007ffd3b0c81218b2c4b39bf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 21 Apr 2026 15:56:38 +0200 Subject: [PATCH 001/471] Add flag for automatic back pagination feature --- .../android/libraries/featureflag/api/FeatureFlags.kt | 8 ++++++++ .../libraries/matrix/impl/RustMatrixClientFactory.kt | 5 +++++ 2 files changed, 13 insertions(+) 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 c3395d1007..d4e52bdcf6 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 @@ -161,4 +161,12 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), + AutomaticBackPagination( + key = "feature.automatic_back_pagination", + title = "Automatic back pagination of rooms", + description = "Allow the app to automatically back paginate in rooms to pre-fetch older messages in background." + + "\nRequires an app restart to take effect.", + defaultValue = { false }, + isFinished = false, + ), } 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 f83efd2736..6cbf122084 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 @@ -105,6 +105,11 @@ class RustMatrixClientFactory( suspend fun create(client: Client): RustMatrixClient { val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens() + // Must be called before creating the sync service, timelines etc. + if (featureFlagService.isFeatureEnabled(FeatureFlags.AutomaticBackPagination)) { + client.enableAutomaticBackpagination() + } + client.setUtdDelegate(UtdTracker(analyticsService)) val syncService = client.syncService() From 5d270068d62c08de2184665908e1335402562f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 21 Apr 2026 16:38:53 +0200 Subject: [PATCH 002/471] Setting version for the release 26.04.4 --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 93e92b81ce..d6a2cbea3d 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 4 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 3 +private const val versionReleaseNumber = 4 object Versions { /** From d38ef5ba103683946c828d03f9cd6f2a6da22eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 21 Apr 2026 16:40:30 +0200 Subject: [PATCH 003/471] Adding fastlane file for version 26.04.4 --- fastlane/metadata/android/en-US/changelogs/202604040.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202604040.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202604040.txt b/fastlane/metadata/android/en-US/changelogs/202604040.txt new file mode 100644 index 0000000000..cbb77b7606 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202604040.txt @@ -0,0 +1,2 @@ +Main changes in this version: several bug fixes. +Full changelog: https://github.com/element-hq/element-x-android/releases From 14054344f4789b49f557d48d64fe2b763cc94d51 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 07:20:07 +0000 Subject: [PATCH 004/471] Update dependency org.jsoup:jsoup to v1.22.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index edbfdc2489..3a1afc3373 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -191,7 +191,7 @@ serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-jso kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0" 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.21.2" +jsoup = "org.jsoup:jsoup:1.22.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.2.0" timber = "com.jakewharton.timber:timber:5.0.1" From e4ccec1cdd8c8de6b59964206119a4ee51148024 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 20 Apr 2026 14:53:05 +0200 Subject: [PATCH 005/471] call: Remove deprecated web<->EX api calls, use the new ones --- .../features/call/impl/utils/WebViewAudioManager.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt index 2d674d21af..a72acb0594 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt @@ -268,10 +268,9 @@ class WebViewAudioManager( private fun setWebViewAndroidNativeBridge() { Timber.d("Adding callback in controls.onAudioPlaybackStarted") webView.evaluateJavascript("controls.onAudioPlaybackStarted = () => { androidNativeBridge.onTrackReady(); };", null) - Timber.d("Adding callback in controls.onOutputDeviceSelect") - webView.evaluateJavascript("controls.onOutputDeviceSelect = (id) => { androidNativeBridge.setOutputDevice(id); };", null) + Timber.d("Adding callback in controls.onAudioDeviceSelect") + webView.evaluateJavascript("controls.onAudioDeviceSelect = (id) => { androidNativeBridge.setAudioDevice(id); };", null) } - /** * Returns the list of available audio devices, sorted by likelihood of it being used for communication. * @@ -296,8 +295,8 @@ class WebViewAudioManager( ) { Timber.d("Updating available audio devices") val deviceList = json.encodeToString(devices) - webView.evaluateJavascript("controls.setAvailableOutputDevices($deviceList);", { - Timber.d("Audio: setAvailableOutputDevices result: $it") + webView.evaluateJavascript("controls.setAvailableAudioDevices($deviceList);", { + Timber.d("Audio: setAvailableAudioDevices result: $it") }) } @@ -402,7 +401,7 @@ private class AndroidWebViewAudioBridge( private val onAudioPlaybackStarted: () -> Unit, ) { @JavascriptInterface - fun setOutputDevice(id: String) { + fun setAudioDevice(id: String) { Timber.d("Audio device selected in webview, id: $id") onAudioDeviceSelected(id) } From aff53c2bc211f77ef354e5fc3ebbe2388e1386a2 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 27 Apr 2026 15:27:27 +0200 Subject: [PATCH 006/471] ktlint format --- .../android/features/call/impl/utils/WebViewAudioManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt index a72acb0594..88c1d95572 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt @@ -271,6 +271,7 @@ class WebViewAudioManager( Timber.d("Adding callback in controls.onAudioDeviceSelect") webView.evaluateJavascript("controls.onAudioDeviceSelect = (id) => { androidNativeBridge.setAudioDevice(id); };", null) } + /** * Returns the list of available audio devices, sorted by likelihood of it being used for communication. * From 88606474770e988cfdcb573cef474cabf824d3b2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Apr 2026 17:01:08 +0200 Subject: [PATCH 007/471] Element Call: remove support for SPA call links. Closes #6578 --- .../call/api/{CallType.kt => CallData.kt} | 25 +- .../call/api/ElementCallEntryPoint.kt | 8 +- .../call/impl/src/main/AndroidManifest.xml | 36 +-- .../call/impl/DefaultElementCallEntryPoint.kt | 14 +- .../RingingCallNotificationCreator.kt | 11 +- .../receivers/DeclineCallBroadcastReceiver.kt | 4 +- .../call/impl/ui/CallScreenPresenter.kt | 82 +++---- .../features/call/impl/ui/CallScreenState.kt | 1 - .../call/impl/ui/CallScreenStateProvider.kt | 2 - .../call/impl/ui/CallTypeExtension.kt | 19 -- .../call/impl/ui/ElementCallActivity.kt | 43 ++-- .../call/impl/ui/IncomingCallActivity.kt | 12 +- .../call/impl/utils/ActiveCallManager.kt | 51 ++-- .../call/impl/utils/CallIntentDataParser.kt | 98 -------- .../call/impl/utils/IntentProvider.kt | 10 +- .../call/DefaultElementCallEntryPointTest.kt | 6 +- .../android/features/call/ui/CallDataTest.kt | 23 ++ .../call/ui/CallScreenPresenterTest.kt | 95 +------- .../android/features/call/ui/CallTypeTest.kt | 45 ---- .../call/utils/CallIntentDataParserTest.kt | 226 ------------------ .../utils/DefaultActiveCallManagerTest.kt | 32 ++- .../call/utils/FakeActiveCallManager.kt | 14 +- .../call/test/FakeElementCallEntryPoint.kt | 14 +- .../messages/impl/MessagesFlowNode.kt | 12 +- .../roomdetails/impl/RoomDetailsFlowNode.kt | 8 +- .../userprofile/impl/UserProfileFlowNode.kt | 4 +- .../NotificationResultProcessor.kt | 8 +- .../DefaultNotificationResultProcessorTest.kt | 8 +- tools/adb/callLinkCustomScheme.sh | 14 -- tools/adb/callLinkCustomScheme2.sh | 14 -- 30 files changed, 203 insertions(+), 736 deletions(-) rename features/call/api/src/main/kotlin/io/element/android/features/call/api/{CallType.kt => CallData.kt} (50%) delete mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallTypeExtension.kt delete mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt create mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt delete mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallTypeTest.kt delete mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/utils/CallIntentDataParserTest.kt delete mode 100755 tools/adb/callLinkCustomScheme.sh delete mode 100755 tools/adb/callLinkCustomScheme2.sh diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CallType.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CallData.kt similarity index 50% rename from features/call/api/src/main/kotlin/io/element/android/features/call/api/CallType.kt rename to features/call/api/src/main/kotlin/io/element/android/features/call/api/CallData.kt index 4b09813418..c1dcf573c6 100644 --- a/features/call/api/src/main/kotlin/io/element/android/features/call/api/CallType.kt +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/CallData.kt @@ -14,22 +14,9 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize -sealed interface CallType : NodeInputs, Parcelable { - @Parcelize - data class ExternalUrl(val url: String) : CallType { - override fun toString(): String { - return "ExternalUrl" - } - } - - @Parcelize - data class RoomCall( - val sessionId: SessionId, - val roomId: RoomId, - val isAudioCall: Boolean - ) : CallType { - override fun toString(): String { - return "RoomCall(sessionId=$sessionId, roomId=$roomId, isAudioCall=$isAudioCall)" - } - } -} +@Parcelize +data class CallData( + val sessionId: SessionId, + val roomId: RoomId, + val isAudioCall: Boolean +) : NodeInputs, Parcelable diff --git a/features/call/api/src/main/kotlin/io/element/android/features/call/api/ElementCallEntryPoint.kt b/features/call/api/src/main/kotlin/io/element/android/features/call/api/ElementCallEntryPoint.kt index caa557f4de..2976635ee2 100644 --- a/features/call/api/src/main/kotlin/io/element/android/features/call/api/ElementCallEntryPoint.kt +++ b/features/call/api/src/main/kotlin/io/element/android/features/call/api/ElementCallEntryPoint.kt @@ -17,13 +17,13 @@ import io.element.android.libraries.matrix.api.core.UserId interface ElementCallEntryPoint { /** * Start a call of the given type. - * @param callType The type of call to start. + * @param callData The data of call to start. */ - fun startCall(callType: CallType) + fun startCall(callData: CallData) /** * Handle an incoming call. - * @param callType The type of call. + * @param callData The data of call. * @param eventId The event id of the event that started the call. * @param senderId The user id of the sender of the event that started the call. * @param roomName The name of the room the call is in. @@ -35,7 +35,7 @@ interface ElementCallEntryPoint { * @param textContent The text content of the notification. If null the default content from the system will be used. */ suspend fun handleIncomingCall( - callType: CallType.RoomCall, + callData: CallData, eventId: EventId, senderId: UserId, roomName: String?, diff --git a/features/call/impl/src/main/AndroidManifest.xml b/features/call/impl/src/main/AndroidManifest.xml index daf1a910c9..c35c6843ff 100644 --- a/features/call/impl/src/main/AndroidManifest.xml +++ b/features/call/impl/src/main/AndroidManifest.xml @@ -30,44 +30,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:taskAffinity="io.element.android.features.call" /> ().inject(this) appCoroutineScope.launch { activeCallManager.hangUpCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = notificationData.sessionId, roomId = notificationData.roomId, isAudioCall = notificationData.audioOnly diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index da2c57c0ac..b9bd6640b4 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -23,7 +23,7 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.compound.theme.ElementTheme -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.data.WidgetMessage import io.element.android.features.call.impl.utils.ActiveCallManager import io.element.android.features.call.impl.utils.CallWidgetProvider @@ -52,7 +52,7 @@ import kotlin.time.Duration.Companion.seconds @AssistedInject class CallScreenPresenter( - @Assisted private val callType: CallType, + @Assisted private val callData: CallData, @Assisted private val navigator: CallScreenNavigator, private val callWidgetProvider: CallWidgetProvider, userAgentProvider: UserAgentProvider, @@ -69,10 +69,9 @@ class CallScreenPresenter( ) : Presenter { @AssistedFactory interface Factory { - fun create(callType: CallType, navigator: CallScreenNavigator): CallScreenPresenter + fun create(callData: CallData, navigator: CallScreenNavigator): CallScreenPresenter } - private val isInWidgetMode = callType is CallType.RoomCall private val userAgent = userAgentProvider.provide() @Composable @@ -90,9 +89,9 @@ class CallScreenPresenter( DisposableEffect(Unit) { coroutineScope.launch { // Sets the call as joined - activeCallManager.joinedCall(callType) + activeCallManager.joinedCall(callData) fetchRoomCallUrl( - inputs = callType, + callData = callData, urlState = urlState, callWidgetDriver = callWidgetDriver, languageTag = languageTag, @@ -100,19 +99,10 @@ class CallScreenPresenter( ) } onDispose { - appCoroutineScope.launch { activeCallManager.hangUpCall(callType) } + appCoroutineScope.launch { activeCallManager.hangUpCall(callData) } } } - - when (callType) { - is CallType.ExternalUrl -> { - // No analytics yet for external calls - } - is CallType.RoomCall -> { - screenTracker.TrackScreen(screen = MobileScreen.ScreenName.RoomCall) - } - } - + screenTracker.TrackScreen(screen = MobileScreen.ScreenName.RoomCall) HandleMatrixClientSyncState() callWidgetDriver.value?.let { driver -> @@ -149,18 +139,15 @@ class CallScreenPresenter( .launchIn(this) } - if (callType is CallType.RoomCall) { - // Note: For external calls isWidgetLoaded will always be false - LaunchedEffect(Unit) { - // Wait for the call to be joined, if it takes too long, we display an error - delay(10.seconds) + LaunchedEffect(Unit) { + // Wait for the call to be joined, if it takes too long, we display an error + delay(10.seconds) - if (!isWidgetLoaded) { - Timber.w("The call took too long to load. Displaying an error before exiting.") + if (!isWidgetLoaded) { + Timber.w("The call took too long to load. Displaying an error before exiting.") - // This will display a simple 'Sorry, an error occurred' dialog and force the user to exit the call - webViewError = "" - } + // This will display a simple 'Sorry, an error occurred' dialog and force the user to exit the call + webViewError = "" } } } @@ -204,37 +191,29 @@ class CallScreenPresenter( webViewError = webViewError, userAgent = userAgent, isCallActive = isWidgetLoaded, - isInWidgetMode = isInWidgetMode, eventSink = ::handleEvent, ) } private suspend fun fetchRoomCallUrl( - inputs: CallType, + callData: CallData, urlState: MutableState>, callWidgetDriver: MutableState, languageTag: String?, theme: String?, ) { urlState.runCatchingUpdatingState { - when (inputs) { - is CallType.ExternalUrl -> { - inputs.url - } - is CallType.RoomCall -> { - val result = callWidgetProvider.getWidget( - sessionId = inputs.sessionId, - roomId = inputs.roomId, - clientId = UUID.randomUUID().toString(), - isAudioCall = inputs.isAudioCall, - languageTag = languageTag, - theme = theme, - ).getOrThrow() - callWidgetDriver.value = result.driver - Timber.d("Call widget driver initialized for sessionId: ${inputs.sessionId}, roomId: ${inputs.roomId}") - result.url - } - } + val result = callWidgetProvider.getWidget( + sessionId = callData.sessionId, + roomId = callData.roomId, + clientId = UUID.randomUUID().toString(), + isAudioCall = callData.isAudioCall, + languageTag = languageTag, + theme = theme, + ).getOrThrow() + callWidgetDriver.value = result.driver + Timber.d("Call widget driver initialized for sessionId: ${callData.sessionId}, roomId: ${callData.roomId}") + result.url } } @@ -242,12 +221,11 @@ class CallScreenPresenter( private fun HandleMatrixClientSyncState() { val coroutineScope = rememberCoroutineScope() DisposableEffect(Unit) { - val roomCallType = callType as? CallType.RoomCall ?: return@DisposableEffect onDispose {} - val client = matrixClientsProvider.getOrNull(roomCallType.sessionId) ?: return@DisposableEffect onDispose { - Timber.w("No MatrixClient found for sessionId, can't send call notification: ${roomCallType.sessionId}") + val client = matrixClientsProvider.getOrNull(callData.sessionId) ?: return@DisposableEffect onDispose { + Timber.w("No MatrixClient found for sessionId, can't send call notification: ${callData.sessionId}") } coroutineScope.launch { - Timber.d("Observing sync state in-call for sessionId: ${roomCallType.sessionId}") + Timber.d("Observing sync state in-call for sessionId: ${callData.sessionId}") client.syncService.syncState .collect { state -> if (state != SyncState.Running) { @@ -256,7 +234,7 @@ class CallScreenPresenter( } } onDispose { - Timber.d("Stopped observing sync state in-call for sessionId: ${roomCallType.sessionId}") + Timber.d("Stopped observing sync state in-call for sessionId: ${callData.sessionId}") // Make sure we mark the call as ended in the app state appForegroundStateService.updateIsInCallState(false) } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt index c07594aebb..3608a2b620 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt @@ -15,6 +15,5 @@ data class CallScreenState( val webViewError: String?, val userAgent: String, val isCallActive: Boolean, - val isInWidgetMode: Boolean, val eventSink: (CallScreenEvents) -> Unit, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt index 3e72f96f87..036bb6103a 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt @@ -26,7 +26,6 @@ internal fun aCallScreenState( webViewError: String? = null, userAgent: String = "", isCallActive: Boolean = true, - isInWidgetMode: Boolean = false, eventSink: (CallScreenEvents) -> Unit = {}, ): CallScreenState { return CallScreenState( @@ -34,7 +33,6 @@ internal fun aCallScreenState( webViewError = webViewError, userAgent = userAgent, isCallActive = isCallActive, - isInWidgetMode = isInWidgetMode, eventSink = eventSink, ) } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallTypeExtension.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallTypeExtension.kt deleted file mode 100644 index 0c18c3e1a4..0000000000 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallTypeExtension.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.impl.ui - -import io.element.android.features.call.api.CallType -import io.element.android.libraries.matrix.api.core.SessionId - -fun CallType.getSessionId(): SessionId? { - return when (this) { - is CallType.ExternalUrl -> null - is CallType.RoomCall -> sessionId - } -} 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 5fa3beb36a..dddcfceb50 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,8 +35,7 @@ import androidx.core.util.Consumer import androidx.lifecycle.Lifecycle import dev.zacsweers.metro.Inject import io.element.android.compound.colors.SemanticColorsLightDark -import io.element.android.features.call.api.CallType -import io.element.android.features.call.api.CallType.ExternalUrl +import io.element.android.features.call.api.CallData 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,7 +43,6 @@ import io.element.android.features.call.impl.pip.PictureInPicturePresenter import io.element.android.features.call.impl.pip.PictureInPictureState 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.features.enterprise.api.EnterpriseService import io.element.android.libraries.androidutils.browser.ConsoleMessageLogger import io.element.android.libraries.architecture.Presenter @@ -64,7 +62,6 @@ class ElementCallActivity : AppCompatActivity(), CallScreenNavigator, PipView { - @Inject lateinit var callIntentDataParser: CallIntentDataParser @Inject lateinit var presenterFactory: CallScreenPresenter.Factory @Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var featureFlagService: FeatureFlagService @@ -80,7 +77,7 @@ class ElementCallActivity : private val requestPermissionsLauncher = registerPermissionResultLauncher() - private val webViewTarget = mutableStateOf(null) + private val webViewTarget = mutableStateOf(null) private var eventSink: ((CallScreenEvents) -> Unit)? = null @@ -98,7 +95,7 @@ class ElementCallActivity : window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) } - setCallType(intent) + setCallData(intent) // If presenter is not created at this point, it means we have no call to display, the Activity is finishing, so return early if (!::presenter.isInitialized) { return @@ -111,8 +108,8 @@ class ElementCallActivity : setContent { val pipState = pictureInPicturePresenter.present() ListenToAndroidEvents(pipState) - val colors by remember(webViewTarget.value?.getSessionId()) { - enterpriseService.semanticColorsFlow(sessionId = webViewTarget.value?.getSessionId()) + val colors by remember(webViewTarget.value?.sessionId) { + enterpriseService.semanticColorsFlow(sessionId = webViewTarget.value?.sessionId) }.collectAsState(SemanticColorsLightDark.default) ElementThemeApp( appPreferencesStore = appPreferencesStore, @@ -123,9 +120,8 @@ class ElementCallActivity : ) { val state = presenter.present() eventSink = state.eventSink - LaunchedEffect(state.isCallActive, state.isInWidgetMode) { - // Note when not in WidgetMode, isCallActive will never be true, so consider the call is active - if (state.isCallActive || !state.isInWidgetMode) { + LaunchedEffect(state.isCallActive) { + if (state.isCallActive) { setCallIsActive() } } @@ -188,7 +184,7 @@ class ElementCallActivity : override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - setCallType(intent) + setCallData(intent) } override fun onDestroy() { @@ -207,25 +203,24 @@ class ElementCallActivity : finish() } - private fun setCallType(intent: Intent?) { - val callType = intent?.let { - IntentCompat.getParcelableExtra(intent, DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, CallType::class.java) - ?: intent.dataString?.let(::parseUrl)?.let(::ExternalUrl) + private fun setCallData(intent: Intent?) { + val callData = intent?.let { + IntentCompat.getParcelableExtra(intent, DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, CallData::class.java) } - val currentCallType = webViewTarget.value - if (currentCallType == null) { - if (callType == null) { + val currentCallData = webViewTarget.value + if (currentCallData == null) { + if (callData == 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 { Timber.tag(loggerTag.value).d("Set the call type and create the presenter") - webViewTarget.value = callType - presenter = presenterFactory.create(callType, this) + webViewTarget.value = callData + presenter = presenterFactory.create(callData, this) } } else { - if (callType == null) { + if (callData == null) { Timber.tag(loggerTag.value).d("Coming back from notification, do nothing") - } else if (callType != currentCallType) { + } else if (callData != currentCallData) { Timber.tag(loggerTag.value).d("User starts another call, restart the Activity") setIntent(intent) recreate() @@ -236,8 +231,6 @@ class ElementCallActivity : } } - private fun parseUrl(url: String?): String? = callIntentDataParser.parse(url) - private fun registerPermissionResultLauncher(): ActivityResultLauncher> { return registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt index 2c4deab65e..1d6989fb3c 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt @@ -19,7 +19,7 @@ import androidx.core.content.IntentCompat import androidx.lifecycle.lifecycleScope import dev.zacsweers.metro.Inject import io.element.android.compound.colors.SemanticColorsLightDark -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.call.impl.di.CallBindings import io.element.android.features.call.impl.notifications.CallNotificationData @@ -118,10 +118,10 @@ class IncomingCallActivity : AppCompatActivity() { private fun onAnswer(notificationData: CallNotificationData) { elementCallEntryPoint.startCall( - CallType.RoomCall( - notificationData.sessionId, - notificationData.roomId, - isAudioCall = notificationData.audioOnly + CallData( + sessionId = notificationData.sessionId, + roomId = notificationData.roomId, + isAudioCall = notificationData.audioOnly, ) ) } @@ -129,7 +129,7 @@ class IncomingCallActivity : AppCompatActivity() { private fun onCancel() { val activeCall = activeCallManager.activeCall.value ?: return appCoroutineScope.launch { - activeCallManager.hangUpCall(callType = activeCall.callType) + activeCallManager.hangUpCall(callData = activeCall.callData) } } } 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 99679a8afb..685fc932fe 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 @@ -20,7 +20,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn import io.element.android.appconfig.ElementCallConfig -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData 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 @@ -73,20 +73,20 @@ interface ActiveCallManager { /** * Called to hang up the active call. It will hang up the call and remove any existing UI and the active call. - * @param callType The type of call that the user hangs up, either an external url one or a room one. + * @param callData The data about the call. * @param notificationData The data for the incoming call notification. */ suspend fun hangUpCall( - callType: CallType, + callData: CallData, notificationData: CallNotificationData? = null, ) /** * Called after the user joined a call. It will remove any existing UI and set the call state as [CallState.InCall]. * - * @param callType The type of call that the user joined, either an external url one or a room one. + * @param callData The data about the call. */ - suspend fun joinedCall(callType: CallType) + suspend fun joinedCall(callData: CallData) } @SingleIn(AppScope::class) @@ -143,7 +143,7 @@ class DefaultActiveCallManager( return } activeCall.value = ActiveCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = notificationData.sessionId, roomId = notificationData.roomId, isAudioCall = notificationData.audioOnly, @@ -198,17 +198,17 @@ class DefaultActiveCallManager( } override suspend fun hangUpCall( - callType: CallType, + callData: CallData, notificationData: CallNotificationData?, ) = mutex.withLock { - Timber.tag(tag).d("Hang up call: $callType") + Timber.tag(tag).d("Hang up call: $callData") cancelIncomingCallNotification() val currentActiveCall = activeCall.value ?: run { // activeCall.value can be null if the application has been killed while the call was ringing // Build a currentActiveCall with the provided parameters. notificationData?.let { ActiveCall( - callType = callType, + callData = callData, callState = CallState.Ringing( notificationData = notificationData, ) @@ -219,8 +219,8 @@ class DefaultActiveCallManager( return@withLock } - if (currentActiveCall.callType != callType) { - Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring") + if (currentActiveCall.callData != callData) { + Timber.tag(tag).w("Call type $callData does not match the active call type, ignoring") return@withLock } if (currentActiveCall.callState is CallState.Ringing) { @@ -244,8 +244,8 @@ class DefaultActiveCallManager( activeCall.value = null } - override suspend fun joinedCall(callType: CallType) = mutex.withLock { - Timber.tag(tag).d("Joined call: $callType") + override suspend fun joinedCall(callData: CallData) = mutex.withLock { + Timber.tag(tag).d("Joined call: $callData") cancelIncomingCallNotification() if (activeWakeLock?.isHeld == true) { Timber.tag(tag).d("Releasing partial wakelock after joining call") @@ -254,7 +254,7 @@ class DefaultActiveCallManager( timedOutCallJob?.cancel() activeCall.value = ActiveCall( - callType = callType, + callData = callData, callState = CallState.InCall, ) } @@ -307,15 +307,15 @@ class DefaultActiveCallManager( private fun observeRingingCall() { activeCall .filterNotNull() - .filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall } + .filter { it.callState is CallState.Ringing } .flatMapLatest { activeCall -> - val callType = activeCall.callType as CallType.RoomCall + val callData = activeCall.callData val ringingInfo = activeCall.callState as CallState.Ringing - val client = matrixClientProvider.getOrRestore(callType.sessionId).getOrNull() ?: run { + val client = matrixClientProvider.getOrRestore(callData.sessionId).getOrNull() ?: run { Timber.tag(tag).d("Couldn't find session for incoming call: $activeCall") return@flatMapLatest flowOf() } - val room = client.getRoom(callType.roomId) ?: run { + val room = client.getRoom(callData.roomId) ?: run { Timber.tag(tag).d("Couldn't find room for incoming call: $activeCall") return@flatMapLatest flowOf() } @@ -346,17 +346,17 @@ class DefaultActiveCallManager( // has joined the call from another session. activeCall .filterNotNull() - .filter { it.callState is CallState.Ringing && it.callType is CallType.RoomCall } + .filter { it.callState is CallState.Ringing } .flatMapLatest { activeCall -> - val callType = activeCall.callType as CallType.RoomCall + val callData = activeCall.callData // Get a flow of updated `hasRoomCall` and `activeRoomCallParticipants` values for the room - val room = matrixClientProvider.getOrRestore(callType.sessionId).getOrNull()?.getRoom(callType.roomId) ?: run { + val room = matrixClientProvider.getOrRestore(callData.sessionId).getOrNull()?.getRoom(callData.roomId) ?: run { Timber.tag(tag).d("Couldn't find room for incoming call: $activeCall") return@flatMapLatest flowOf() } room.roomInfoFlow.map { Timber.tag(tag).d("Has room call status changed for ringing call: ${it.hasRoomCall}") - it.hasRoomCall to (callType.sessionId in it.activeRoomCallParticipants) + it.hasRoomCall to (callData.sessionId in it.activeRoomCallParticipants) } } // We only want to check if the room active call status changes @@ -388,10 +388,7 @@ class DefaultActiveCallManager( // 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)) - } + defaultCurrentCallService.onCallStarted(CurrentCall.RoomCall(value.callData.roomId)) } } } @@ -404,7 +401,7 @@ class DefaultActiveCallManager( * Represents an active call. */ data class ActiveCall( - val callType: CallType, + val callData: CallData, val callState: CallState, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt deleted file mode 100644 index f5433c15a0..0000000000 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/CallIntentDataParser.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.impl.utils - -import android.net.Uri -import androidx.core.net.toUri -import dev.zacsweers.metro.Inject - -@Inject -class CallIntentDataParser { - private val validHttpSchemes = sequenceOf("https") - private val knownHosts = sequenceOf( - "call.element.io", - ) - - fun parse(data: String?): String? { - val parsedUrl = data?.toUri() ?: return null - val scheme = parsedUrl.scheme - return when { - scheme in validHttpSchemes -> parsedUrl - scheme == "element" && parsedUrl.host == "call" -> { - parsedUrl.getUrlParameter() - } - scheme == "io.element.call" && parsedUrl.host == null -> { - parsedUrl.getUrlParameter() - } - // This should never be possible, but we still need to take into account the possibility - else -> null - } - ?.takeIf { it.host in knownHosts } - ?.withCustomParameters() - } - - private fun Uri.getUrlParameter(): Uri? { - return getQueryParameter("url") - ?.let { urlParameter -> - urlParameter.toUri().takeIf { uri -> - uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank() - } - } - } -} - -/** - * Ensure the uri has the following parameters and value in the fragment: - * - appPrompt=false - * - confineToRoom=true - * to ensure that the rendering will bo correct on the embedded Webview. - */ -private fun Uri.withCustomParameters(): String { - val builder = buildUpon() - // Remove the existing query parameters - builder.clearQuery() - queryParameterNames.forEach { - if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach - builder.appendQueryParameter(it, getQueryParameter(it)) - } - // Remove the existing fragment parameters, and build the new fragment - val currentFragment = fragment ?: "" - // Reset the current fragment - builder.fragment("") - val queryFragmentPosition = currentFragment.lastIndexOf("?") - val newFragment = if (queryFragmentPosition == -1) { - // No existing query, build it. - "$currentFragment?$APP_PROMPT_PARAMETER=false&$CONFINE_TO_ROOM_PARAMETER=true" - } else { - buildString { - append(currentFragment.substring(0, queryFragmentPosition + 1)) - val queryFragment = currentFragment.substring(queryFragmentPosition + 1) - // Replace the existing parameters - val newQueryFragment = queryFragment - .replace("$APP_PROMPT_PARAMETER=true", "$APP_PROMPT_PARAMETER=false") - .replace("$CONFINE_TO_ROOM_PARAMETER=false", "$CONFINE_TO_ROOM_PARAMETER=true") - append(newQueryFragment) - // Ensure the parameters are there - if (!newQueryFragment.contains("$APP_PROMPT_PARAMETER=false")) { - if (newQueryFragment.isNotEmpty()) { - append("&") - } - append("$APP_PROMPT_PARAMETER=false") - } - if (!newQueryFragment.contains("$CONFINE_TO_ROOM_PARAMETER=true")) { - append("&$CONFINE_TO_ROOM_PARAMETER=true") - } - } - } - // We do not want to encode the Fragment part, so append it manually - return builder.build().toString() + "#" + newFragment -} - -private const val APP_PROMPT_PARAMETER = "appPrompt" -private const val CONFINE_TO_ROOM_PARAMETER = "confineToRoom" diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/IntentProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/IntentProvider.kt index 0f74ba86d4..c6c607cbbc 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/IntentProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/IntentProvider.kt @@ -12,21 +12,21 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.PendingIntentCompat -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.DefaultElementCallEntryPoint import io.element.android.features.call.impl.ui.ElementCallActivity internal object IntentProvider { - fun createIntent(context: Context, callType: CallType): Intent = Intent(context, ElementCallActivity::class.java).apply { - putExtra(DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, callType) + fun createIntent(context: Context, callData: CallData): Intent = Intent(context, ElementCallActivity::class.java).apply { + putExtra(DefaultElementCallEntryPoint.EXTRA_CALL_TYPE, callData) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_USER_ACTION) } - fun getPendingIntent(context: Context, callType: CallType): PendingIntent { + fun getPendingIntent(context: Context, callData: CallData): PendingIntent { return PendingIntentCompat.getActivity( context, DefaultElementCallEntryPoint.REQUEST_CODE, - createIntent(context, callType), + createIntent(context, callData), PendingIntent.FLAG_CANCEL_CURRENT, false )!! diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt index 85cec8c586..f21447cc85 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt @@ -11,7 +11,7 @@ package io.element.android.features.call import android.content.Intent import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.DefaultElementCallEntryPoint import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.ui.ElementCallActivity @@ -37,7 +37,7 @@ class DefaultElementCallEntryPointTest { @Test fun `startCall - starts ElementCallActivity setup with the needed extras`() = runTest { val entryPoint = createEntryPoint() - entryPoint.startCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, isAudioCall = false)) + entryPoint.startCall(CallData(A_SESSION_ID, A_ROOM_ID, isAudioCall = false)) val expectedIntent = Intent(InstrumentationRegistry.getInstrumentation().targetContext, ElementCallActivity::class.java) val intent = shadowOf(RuntimeEnvironment.getApplication()).nextStartedActivity @@ -53,7 +53,7 @@ class DefaultElementCallEntryPointTest { val entryPoint = createEntryPoint(activeCallManager = activeCallManager) entryPoint.handleIncomingCall( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, isAudioCall = false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, isAudioCall = false), eventId = AN_EVENT_ID, senderId = A_USER_ID_2, roomName = "roomName", diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt new file mode 100644 index 0000000000..f0cdd44082 --- /dev/null +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.ui + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.call.api.CallData +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import org.junit.Test + +class CallDataTest { + @Test + fun `RoomCall stringification does not contain the URL`() { + assertThat(CallData(A_SESSION_ID, A_ROOM_ID, false).toString()) + .isEqualTo("CallData(sessionId=$A_SESSION_ID, roomId=$A_ROOM_ID, isAudioCall=false)") + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index b6b0120451..c2c576999c 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -13,7 +13,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.MobileScreen -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.ui.CallScreenEvents import io.element.android.features.call.impl.ui.CallScreenNavigator import io.element.android.features.call.impl.ui.CallScreenPresenter @@ -59,38 +59,13 @@ class CallScreenPresenterTest { val warmUpRule = WarmUpRule() @Test - fun `present - with CallType ExternalUrl just loads the URL and sets the call as active`() = runTest { - val analyticsLambda = lambdaRecorder {} - val joinedCallLambda = lambdaRecorder {} - val presenter = createCallScreenPresenter( - callType = CallType.ExternalUrl("https://call.element.io"), - screenTracker = FakeScreenTracker(analyticsLambda), - activeCallManager = FakeActiveCallManager(joinedCallResult = joinedCallLambda), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - // Wait until the URL is loaded - advanceTimeBy(1.seconds) - skipItems(2) - val initialState = awaitItem() - assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io")) - assertThat(initialState.webViewError).isNull() - assertThat(initialState.isInWidgetMode).isFalse() - assertThat(initialState.isCallActive).isFalse() - analyticsLambda.assertions().isNeverCalled() - joinedCallLambda.assertions().isCalledOnce() - } - } - - @Test - fun `present - with CallType RoomCall sets call as active, loads URL and runs WidgetDriver`() = runTest { + fun `present - with CallData sets call as active, loads URL and runs WidgetDriver`() = runTest { val widgetDriver = FakeMatrixWidgetDriver() val widgetProvider = FakeCallWidgetProvider(widgetDriver) val analyticsLambda = lambdaRecorder {} - val joinedCallLambda = lambdaRecorder {} + val joinedCallLambda = lambdaRecorder {} val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, widgetProvider = widgetProvider, screenTracker = FakeScreenTracker(analyticsLambda), @@ -107,7 +82,6 @@ class CallScreenPresenterTest { val initialState = awaitItem() assertThat(initialState.urlState).isInstanceOf(AsyncData.Loading::class.java) assertThat(initialState.isCallActive).isFalse() - assertThat(initialState.isInWidgetMode).isTrue() assertThat(widgetProvider.getWidgetCalled).isTrue() assertThat(widgetDriver.runCalledCount).isEqualTo(1) analyticsLambda.assertions().isCalledOnce().with(value(MobileScreen.ScreenName.RoomCall)) @@ -123,7 +97,7 @@ class CallScreenPresenterTest { fun `present - set message interceptor, send and receive messages`() = runTest { val widgetDriver = FakeMatrixWidgetDriver() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, screenTracker = FakeScreenTracker {}, ) @@ -154,7 +128,7 @@ class CallScreenPresenterTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, navigator = navigator, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), @@ -188,7 +162,7 @@ class CallScreenPresenterTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, navigator = navigator, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), @@ -223,7 +197,7 @@ class CallScreenPresenterTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, navigator = navigator, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), @@ -260,7 +234,7 @@ class CallScreenPresenterTest { val navigator = FakeCallScreenNavigator() val widgetDriver = FakeMatrixWidgetDriver() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, navigator = navigator, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), @@ -300,7 +274,7 @@ class CallScreenPresenterTest { val matrixClient = FakeMatrixClient(syncService = syncService) val appForegroundStateService = FakeAppForegroundStateService() val presenter = createCallScreenPresenter( - callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false), + callData = CallData(A_SESSION_ID, A_ROOM_ID, false), widgetDriver = widgetDriver, navigator = navigator, dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), @@ -338,53 +312,8 @@ class CallScreenPresenterTest { } } - @Test - fun `present - error from WebView are updating the state`() = runTest { - val presenter = createCallScreenPresenter( - callType = CallType.ExternalUrl("https://call.element.io"), - activeCallManager = FakeActiveCallManager(), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - // Wait until the URL is loaded - advanceTimeBy(1.seconds) - skipItems(2) - val initialState = awaitItem() - initialState.eventSink(CallScreenEvents.OnWebViewError("A Webview error")) - val finalState = awaitItem() - assertThat(finalState.webViewError).isEqualTo("A Webview error") - } - } - - @Test - fun `present - error from WebView are ignored if Element Call is loaded`() = runTest { - val presenter = createCallScreenPresenter( - callType = CallType.ExternalUrl("https://call.element.io"), - activeCallManager = FakeActiveCallManager(), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - // Wait until the URL is loaded - skipItems(1) - val initialState = awaitItem() - - val messageInterceptor = FakeWidgetMessageInterceptor() - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) - // Emit a message - messageInterceptor.givenInterceptedMessage("A message") - // WebView emits an error, but it will be ignored - initialState.eventSink(CallScreenEvents.OnWebViewError("A Webview error")) - val finalState = awaitItem() - assertThat(finalState.webViewError).isNull() - - cancelAndIgnoreRemainingEvents() - } - } - private fun TestScope.createCallScreenPresenter( - callType: CallType, + callData: CallData, navigator: CallScreenNavigator = FakeCallScreenNavigator(), widgetDriver: FakeMatrixWidgetDriver = FakeMatrixWidgetDriver(), widgetProvider: FakeCallWidgetProvider = FakeCallWidgetProvider(widgetDriver), @@ -401,7 +330,7 @@ class CallScreenPresenterTest { } val clock = SystemClock { 0 } return CallScreenPresenter( - callType = callType, + callData = callData, navigator = navigator, callWidgetProvider = widgetProvider, userAgentProvider = userAgentProvider, diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallTypeTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallTypeTest.kt deleted file mode 100644 index c83408bd3b..0000000000 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallTypeTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.ui - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType -import io.element.android.features.call.impl.ui.getSessionId -import io.element.android.libraries.matrix.test.A_ROOM_ID -import io.element.android.libraries.matrix.test.A_SESSION_ID -import org.junit.Test - -class CallTypeTest { - @Test - fun `getSessionId returns null for ExternalUrl`() { - assertThat(CallType.ExternalUrl("aURL").getSessionId()).isNull() - } - - @Test - fun `getSessionId returns the sessionId for RoomCall`() { - assertThat( - CallType.RoomCall( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - isAudioCall = false, - ).getSessionId() - ).isEqualTo(A_SESSION_ID) - } - - @Test - fun `ExternalUrl stringification does not contain the URL`() { - assertThat(CallType.ExternalUrl("aURL").toString()).isEqualTo("ExternalUrl") - } - - @Test - fun `RoomCall stringification does not contain the URL`() { - assertThat(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false).toString()) - .isEqualTo("RoomCall(sessionId=$A_SESSION_ID, roomId=$A_ROOM_ID, isAudioCall=false)") - } -} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/CallIntentDataParserTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/CallIntentDataParserTest.kt deleted file mode 100644 index 43f7f931f1..0000000000 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/CallIntentDataParserTest.kt +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.utils - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.impl.utils.CallIntentDataParser -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import java.net.URLEncoder - -@RunWith(RobolectricTestRunner::class) -class CallIntentDataParserTest { - private val callIntentDataParser = CallIntentDataParser() - - @Test - fun `a null data returns null`() { - val url: String? = null - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `empty data returns null`() { - doTest("", null) - } - - @Test - fun `invalid data returns null`() { - doTest("!", null) - } - - @Test - fun `data with no scheme returns null`() { - doTest("test", null) - } - - @Test - fun `Element Call http urls returns null`() { - doTest("http://call.element.io", null) - doTest("http://call.element.io/some-actual-call?with=parameters", null) - } - - @Test - fun `Element Call urls with unknown host returns null`() { - // Check valid host first, should not return null - doTest("https://call.element.io", "https://call.element.io#?appPrompt=false&confineToRoom=true") - // Unknown host should return null - doTest("https://unknown.io", null) - doTest("https://call.unknown.io", null) - doTest("https://call.element.com", null) - doTest("https://call.element.io.tld", null) - } - - @Test - fun `Element Call urls will be returned as is`() { - doTest( - url = "https://call.element.io", - expectedResult = "https://call.element.io#?$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with url param gets url extracted`() { - doTest( - url = VALID_CALL_URL_WITH_PARAM, - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS" - ) - } - - @Test - fun `HTTP and HTTPS urls that don't come from EC return null`() { - doTest("http://app.element.io", null) - doTest("https://app.element.io", null) - doTest("http://", null) - doTest("https://", null) - } - - @Test - fun `Element Call url with no url returns null`() { - val embeddedUrl = VALID_CALL_URL_WITH_PARAM - val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") - val url = "io.element.call:/?no_url=$encodedUrl" - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `element scheme with no call host returns null`() { - val embeddedUrl = VALID_CALL_URL_WITH_PARAM - val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") - val url = "element://no-call?url=$encodedUrl" - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `element scheme with no data returns null`() { - val url = "element://call?url=" - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `Element Call url with no data returns null`() { - val url = "io.element.call:/?url=" - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `element invalid scheme returns null`() { - val embeddedUrl = VALID_CALL_URL_WITH_PARAM - val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") - val url = "bad.scheme:/?url=$encodedUrl" - assertThat(callIntentDataParser.parse(url)).isNull() - } - - @Test - fun `Element Call url with url extra param appPrompt gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM&appPrompt=true", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with url extra param in fragment appPrompt gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=true", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&confineToRoom=true" - ) - } - - @Test - fun `Element Call url with url extra param in fragment appPrompt and other gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=true&otherParam=maybe", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&otherParam=maybe&confineToRoom=true" - ) - } - - @Test - fun `Element Call url with url extra param confineToRoom gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM&confineToRoom=false", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with url extra param in fragment confineToRoom gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=false", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&appPrompt=false" - ) - } - - @Test - fun `Element Call url with url extra param in fragment confineToRoom and more gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=false&otherParam=maybe", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&otherParam=maybe&appPrompt=false" - ) - } - - @Test - fun `Element Call url with url fragment gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#fragment", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with url fragment with params gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#fragment?otherParam=maybe", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?otherParam=maybe&$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with url fragment with other params gets url extracted`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?otherParam=maybe", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?otherParam=maybe&$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with empty fragment`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS" - ) - } - - @Test - fun `Element Call url with empty fragment query`() { - doTest( - url = "$VALID_CALL_URL_WITH_PARAM#?", - expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS" - ) - } - - private fun doTest(url: String, expectedResult: String?) { - // Test direct parsing - assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult) - - // Test embedded url, scheme 1 - val encodedUrl = URLEncoder.encode(url, "utf-8") - val urlScheme1 = "element://call?url=$encodedUrl" - assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult) - - // Test embedded url, scheme 2 - val urlScheme2 = "io.element.call:/?url=$encodedUrl" - assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult) - } - - companion object { - const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters" - const val EXTRA_PARAMS = "appPrompt=false&confineToRoom=true" - } -} 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 f9f6206ec7..3712904b03 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 @@ -13,7 +13,7 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.features.call.impl.notifications.aCallNotificationData import io.element.android.features.call.impl.utils.ActiveCall @@ -77,7 +77,7 @@ class DefaultActiveCallManagerTest { assertThat(manager.activeCall.value).isEqualTo( ActiveCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = callNotificationData.sessionId, roomId = callNotificationData.roomId, isAudioCall = false, @@ -104,7 +104,7 @@ class DefaultActiveCallManagerTest { assertThat(manager.activeCall.value).isEqualTo( ActiveCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = callNotificationData.sessionId, roomId = callNotificationData.roomId, isAudioCall = true, @@ -132,7 +132,7 @@ class DefaultActiveCallManagerTest { manager.registerIncomingCall(aCallNotificationData(roomId = A_ROOM_ID_2)) assertThat(manager.activeCall.value).isEqualTo(activeCall) - assertThat((manager.activeCall.value?.callType as? CallType.RoomCall)?.roomId).isNotEqualTo(A_ROOM_ID_2) + assertThat(manager.activeCall.value?.callData?.roomId).isNotEqualTo(A_ROOM_ID_2) advanceTimeBy(1) @@ -178,7 +178,7 @@ class DefaultActiveCallManagerTest { } @Test - fun `hangUpCall - removes existing call if the CallType matches`() = runTest { + fun `hangUpCall - removes existing call if the CallData matches`() = runTest { setupShadowPowerManager() val notificationManagerCompat = mockk(relaxed = true) val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat) @@ -188,7 +188,7 @@ class DefaultActiveCallManagerTest { assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeWakeLock?.isHeld).isTrue() - manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false)) + manager.hangUpCall(CallData(notificationData.sessionId, notificationData.roomId, false)) assertThat(manager.activeCall.value).isNull() assertThat(manager.activeWakeLock?.isHeld).isFalse() @@ -215,7 +215,7 @@ class DefaultActiveCallManagerTest { val notificationData = aCallNotificationData(roomId = A_ROOM_ID) manager.registerIncomingCall(notificationData) - manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false)) + manager.hangUpCall(CallData(notificationData.sessionId, notificationData.roomId, false)) coVerify { room.declineCall(notificationEventId = notificationData.eventId) @@ -242,7 +242,7 @@ class DefaultActiveCallManagerTest { val notificationData = aCallNotificationData(roomId = A_ROOM_ID) // Do not register the incoming call, so the manager doesn't know about it manager.hangUpCall( - callType = CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false), + callData = CallData(notificationData.sessionId, notificationData.roomId, false), notificationData = notificationData, ) coVerify { @@ -320,7 +320,7 @@ class DefaultActiveCallManagerTest { } @Test - fun `hangUpCall - does nothing if the CallType doesn't match`() = runTest { + fun `hangUpCall - does nothing if the CallData doesn't match`() = runTest { setupShadowPowerManager() val notificationManagerCompat = mockk(relaxed = true) val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat) @@ -329,7 +329,13 @@ class DefaultActiveCallManagerTest { assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeWakeLock?.isHeld).isTrue() - manager.hangUpCall(CallType.ExternalUrl("https://example.com")) + manager.hangUpCall( + CallData( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID_2, + isAudioCall = true, + ) + ) assertThat(manager.activeCall.value).isNotNull() assertThat(manager.activeWakeLock?.isHeld).isTrue() @@ -344,10 +350,10 @@ class DefaultActiveCallManagerTest { val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat) assertThat(manager.activeCall.value).isNull() - manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, true)) + manager.joinedCall(CallData(A_SESSION_ID, A_ROOM_ID, true)) assertThat(manager.activeCall.value).isEqualTo( ActiveCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = A_SESSION_ID, roomId = A_ROOM_ID, isAudioCall = true, @@ -450,7 +456,7 @@ class DefaultActiveCallManagerTest { assertThat(manager.activeCall.value).isEqualTo( ActiveCall( - callType = CallType.RoomCall( + callData = CallData( sessionId = callNotificationData.sessionId, roomId = callNotificationData.roomId, isAudioCall = false, diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt index 2d0e126ab5..c2c38284a9 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt @@ -8,7 +8,7 @@ package io.element.android.features.call.utils -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.utils.ActiveCall import io.element.android.features.call.impl.utils.ActiveCallManager @@ -17,8 +17,8 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeActiveCallManager( var registerIncomingCallResult: (CallNotificationData) -> Unit = {}, - var hangUpCallResult: (CallType, CallNotificationData?) -> Unit = { _, _ -> }, - var joinedCallResult: (CallType) -> Unit = {}, + var hangUpCallResult: (CallData, CallNotificationData?) -> Unit = { _, _ -> }, + var joinedCallResult: (CallData) -> Unit = {}, ) : ActiveCallManager { override val activeCall = MutableStateFlow(null) @@ -26,12 +26,12 @@ class FakeActiveCallManager( registerIncomingCallResult(notificationData) } - override suspend fun hangUpCall(callType: CallType, notificationData: CallNotificationData?) = simulateLongTask { - hangUpCallResult(callType, notificationData) + override suspend fun hangUpCall(callData: CallData, notificationData: CallNotificationData?) = simulateLongTask { + hangUpCallResult(callData, notificationData) } - override suspend fun joinedCall(callType: CallType) = simulateLongTask { - joinedCallResult(callType) + override suspend fun joinedCall(callData: CallData) = simulateLongTask { + joinedCallResult(callData) } fun setActiveCall(value: ActiveCall?) { diff --git a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeElementCallEntryPoint.kt b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeElementCallEntryPoint.kt index fdf3ca566b..13b61feacb 100644 --- a/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeElementCallEntryPoint.kt +++ b/features/call/test/src/main/kotlin/io/element/android/features/call/test/FakeElementCallEntryPoint.kt @@ -8,16 +8,16 @@ package io.element.android.features.call.test -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.tests.testutils.lambda.lambdaError class FakeElementCallEntryPoint( - var startCallResult: (CallType) -> Unit = { lambdaError() }, + var startCallResult: (CallData) -> Unit = { lambdaError() }, var handleIncomingCallResult: ( - CallType.RoomCall, + CallData, EventId, UserId, String?, @@ -27,12 +27,12 @@ class FakeElementCallEntryPoint( String?, ) -> Unit = { _, _, _, _, _, _, _, _ -> lambdaError() } ) : ElementCallEntryPoint { - override fun startCall(callType: CallType) { - startCallResult(callType) + override fun startCall(callData: CallData) { + startCallResult(callData) } override suspend fun handleIncomingCall( - callType: CallType.RoomCall, + callData: CallData, eventId: EventId, senderId: UserId, roomName: String?, @@ -44,7 +44,7 @@ class FakeElementCallEntryPoint( textContent: String?, ) { handleIncomingCallResult( - callType, + callData, eventId, senderId, roomName, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 36e94ec456..c7b046dc14 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -24,7 +24,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.annotations.ContributesNode -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint @@ -277,13 +277,13 @@ class MessagesFlowNode( } override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { - val callType = CallType.RoomCall( + val callData = CallData( sessionId = sessionId, roomId = roomId, - isAudioCall = isAudioCall + isAudioCall = isAudioCall, ) analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton) - elementCallEntryPoint.startCall(callType) + elementCallEntryPoint.startCall(callData) } override fun navigateToPinnedMessagesList() { @@ -506,13 +506,13 @@ class MessagesFlowNode( } override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { - val callType = CallType.RoomCall( + val callData = CallData( sessionId = sessionId, roomId = roomId, isAudioCall = isAudioCall ) analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton) - elementCallEntryPoint.startCall(callType) + elementCallEntryPoint.startCall(callData) } override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index c3ae902ba9..818287ab68 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -25,7 +25,7 @@ import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint @@ -225,13 +225,13 @@ class RoomDetailsFlowNode( } override fun navigateToRoomCall(callIntent: CallIntent) { - val inputs = CallType.RoomCall( + val callData = CallData( sessionId = room.sessionId, roomId = room.roomId, isAudioCall = callIntent == CallIntent.AUDIO ) analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton) - elementCallEntryPoint.startCall(inputs) + elementCallEntryPoint.startCall(callData) } override fun navigateToReportRoom() { @@ -288,7 +288,7 @@ class RoomDetailsFlowNode( override fun startCall(dmRoomId: RoomId, callIntent: CallIntent) { elementCallEntryPoint.startCall( - CallType.RoomCall( + CallData( roomId = dmRoomId, sessionId = room.sessionId, isAudioCall = callIntent == CallIntent.AUDIO diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt index aaafbe04be..aff9e3502d 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/UserProfileFlowNode.kt @@ -20,7 +20,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.userprofile.impl.root.UserProfileNode @@ -86,7 +86,7 @@ class UserProfileFlowNode( override fun startCall(dmRoomId: RoomId, callIntent: CallIntent) { elementCallEntryPoint.startCall( - CallType.RoomCall( + CallData( sessionId = sessionId, roomId = dmRoomId, isAudioCall = callIntent == CallIntent.AUDIO diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt index 0dd2761446..a97937f5d0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationResultProcessor.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.push.impl.notifications import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.matrix.api.core.EventId @@ -215,9 +215,9 @@ class DefaultNotificationResultProcessor( private suspend fun handleRingingCallEvent(notifiableEvent: NotifiableRingingCallEvent) { Timber.i("## handleInternal() : Incoming call.") elementCallEntryPoint.handleIncomingCall( - callType = CallType.RoomCall( - notifiableEvent.sessionId, - notifiableEvent.roomId, + callData = CallData( + sessionId = notifiableEvent.sessionId, + roomId = notifiableEvent.roomId, isAudioCall = notifiableEvent.callIntent == CallIntent.AUDIO ), eventId = notifiableEvent.eventId, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt index 6acb375bac..91f29dd28e 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationResultProcessorTest.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.push.impl.notifications import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType +import io.element.android.features.call.api.CallData import io.element.android.features.call.test.FakeElementCallEntryPoint import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -104,7 +104,7 @@ class DefaultNotificationResultProcessorTest { @Test fun `when ringing call PushData is received, the incoming call will be handled`() = runTest { val handleIncomingCallLambda = lambdaRecorder< - CallType.RoomCall, + CallData, EventId, UserId, String?, @@ -140,7 +140,7 @@ class DefaultNotificationResultProcessorTest { fun `when notify call PushData is received, the incoming call will be treated as a normal notification`() = runTest { val onNotifiableEventsReceived = lambdaRecorder, Unit> {} val handleIncomingCallLambda = lambdaRecorder< - CallType.RoomCall, + CallData, EventId, UserId, String?, @@ -176,7 +176,7 @@ class DefaultNotificationResultProcessorTest { fun `when notify call PushData is received, the incoming call will be treated as a normal notification even if notification are disabled`() = runTest { val onNotifiableEventsReceived = lambdaRecorder, Unit> {} val handleIncomingCallLambda = lambdaRecorder< - CallType.RoomCall, + CallData, EventId, UserId, String?, diff --git a/tools/adb/callLinkCustomScheme.sh b/tools/adb/callLinkCustomScheme.sh deleted file mode 100755 index 7e6c9f02d3..0000000000 --- a/tools/adb/callLinkCustomScheme.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -# Copyright (c) 2025 Element Creations Ltd. -# Copyright 2023-2024 New Vector Ltd. -# -# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -# Please see LICENSE files in the repository root for full details. - -# Format is: -# element://call?url=some-encoded-url -# For instance -# element://call?url=https%3A%2F%2Fcall.element.io%2FTestElementCall - -adb shell am start -a android.intent.action.VIEW -d element://call?url=https%3A%2F%2Fcall.element.io%2FTestElementCall diff --git a/tools/adb/callLinkCustomScheme2.sh b/tools/adb/callLinkCustomScheme2.sh deleted file mode 100755 index 43f427f22f..0000000000 --- a/tools/adb/callLinkCustomScheme2.sh +++ /dev/null @@ -1,14 +0,0 @@ -#! /bin/bash - -# Copyright (c) 2025 Element Creations Ltd. -# Copyright 2023-2024 New Vector Ltd. -# -# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -# Please see LICENSE files in the repository root for full details. - -# Format is: -# io.element.call:/?url=some-encoded-url -# For instance -# io.element.call:/?url=https%3A%2F%2Fcall.element.io%2FTestElementCall - -adb shell am start -a android.intent.action.VIEW -d io.element.call:/?url=https%3A%2F%2Fcall.element.io%2FTestElementCall From 2bcf10dd0bb78fe34309e76c0fca3f4e68dad5c9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Apr 2026 17:04:12 +0200 Subject: [PATCH 008/471] `CallScreenEvents` -> `CallScreenEvent` --- .../ui/{CallScreenEvents.kt => CallScreenEvent.kt} | 8 ++++---- .../features/call/impl/ui/CallScreenPresenter.kt | 8 ++++---- .../features/call/impl/ui/CallScreenState.kt | 2 +- .../call/impl/ui/CallScreenStateProvider.kt | 2 +- .../features/call/impl/ui/CallScreenView.kt | 10 +++++----- .../features/call/impl/ui/ElementCallActivity.kt | 6 +++--- .../features/call/ui/CallScreenPresenterTest.kt | 14 +++++++------- 7 files changed, 25 insertions(+), 25 deletions(-) rename features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/{CallScreenEvents.kt => CallScreenEvent.kt} (78%) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvents.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvent.kt similarity index 78% rename from features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvents.kt rename to features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvent.kt index 8fbbce896f..357559c3f9 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvents.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenEvent.kt @@ -10,8 +10,8 @@ package io.element.android.features.call.impl.ui import io.element.android.features.call.impl.utils.WidgetMessageInterceptor -sealed interface CallScreenEvents { - data object Hangup : CallScreenEvents - data class SetupMessageChannels(val widgetMessageInterceptor: WidgetMessageInterceptor) : CallScreenEvents - data class OnWebViewError(val description: String?) : CallScreenEvents +sealed interface CallScreenEvent { + data object Hangup : CallScreenEvent + data class SetupMessageChannels(val widgetMessageInterceptor: WidgetMessageInterceptor) : CallScreenEvent + data class OnWebViewError(val description: String?) : CallScreenEvent } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index b9bd6640b4..7d8e20967f 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -152,9 +152,9 @@ class CallScreenPresenter( } } - fun handleEvent(event: CallScreenEvents) { + fun handleEvent(event: CallScreenEvent) { when (event) { - is CallScreenEvents.Hangup -> { + is CallScreenEvent.Hangup -> { val widgetId = callWidgetDriver.value?.id val interceptor = messageInterceptor.value if (widgetId != null && interceptor != null && isWidgetLoaded) { @@ -174,10 +174,10 @@ class CallScreenPresenter( } } } - is CallScreenEvents.SetupMessageChannels -> { + is CallScreenEvent.SetupMessageChannels -> { messageInterceptor.value = event.widgetMessageInterceptor } - is CallScreenEvents.OnWebViewError -> { + is CallScreenEvent.OnWebViewError -> { if (!ignoreWebViewError) { webViewError = event.description.orEmpty() } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt index 3608a2b620..86b4cc439f 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenState.kt @@ -15,5 +15,5 @@ data class CallScreenState( val webViewError: String?, val userAgent: String, val isCallActive: Boolean, - val eventSink: (CallScreenEvents) -> Unit, + val eventSink: (CallScreenEvent) -> Unit, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt index 036bb6103a..155c5d3380 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenStateProvider.kt @@ -26,7 +26,7 @@ internal fun aCallScreenState( webViewError: String? = null, userAgent: String = "", isCallActive: Boolean = true, - eventSink: (CallScreenEvents) -> Unit = {}, + eventSink: (CallScreenEvent) -> Unit = {}, ): CallScreenState { return CallScreenState( urlState = urlState, 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 f8657a9ece..a945f3c844 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 @@ -68,7 +68,7 @@ internal fun CallScreenView( if (pipState.supportPip) { pipState.eventSink.invoke(PictureInPictureEvents.EnterPictureInPicture) } else { - state.eventSink(CallScreenEvents.Hangup) + state.eventSink(CallScreenEvent.Hangup) } } @@ -84,7 +84,7 @@ internal fun CallScreenView( append(stringResource(CommonStrings.error_unknown)) state.webViewError.takeIf { it.isNotEmpty() }?.let { append("\n\n").append(it) } }, - onSubmit = { state.eventSink(CallScreenEvents.Hangup) }, + onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, ) } else { var webViewAudioManager by remember { mutableStateOf(null) } @@ -123,14 +123,14 @@ internal fun CallScreenView( Timber.d("Can't start in-call audio mode since the app is already in it.") } }, - onError = { state.eventSink(CallScreenEvents.OnWebViewError(it)) }, + onError = { state.eventSink(CallScreenEvent.OnWebViewError(it)) }, ) webViewAudioManager = WebViewAudioManager( webView = webView, coroutineScope = coroutineScope, onInvalidAudioDeviceAdded = { invalidAudioDeviceReason = it }, ) - state.eventSink(CallScreenEvents.SetupMessageChannels(interceptor)) + state.eventSink(CallScreenEvent.SetupMessageChannels(interceptor)) val pipController = WebViewPipController(webView) pipState.eventSink(PictureInPictureEvents.SetPipController(pipController)) }, @@ -147,7 +147,7 @@ internal fun CallScreenView( Timber.e(state.urlState.error, "WebView failed to load URL: ${state.urlState.error.message}") ErrorDialog( content = state.urlState.error.message.orEmpty(), - onSubmit = { state.eventSink(CallScreenEvents.Hangup) }, + onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, ) } is AsyncData.Success -> Unit 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 dddcfceb50..bce882ec2d 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 @@ -79,7 +79,7 @@ class ElementCallActivity : private val webViewTarget = mutableStateOf(null) - private var eventSink: ((CallScreenEvents) -> Unit)? = null + private var eventSink: ((CallScreenEvent) -> Unit)? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -172,7 +172,7 @@ class ElementCallActivity : pipEventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(isInPictureInPictureMode)) if (!isInPictureInPictureMode && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { Timber.tag(loggerTag.value).d("Exiting PiP mode: Hangup the call") - eventSink?.invoke(CallScreenEvents.Hangup) + eventSink?.invoke(CallScreenEvent.Hangup) } } addOnPictureInPictureModeChangedListener(onPictureInPictureModeChangedListener) @@ -280,7 +280,7 @@ class ElementCallActivity : } override fun hangUp() { - eventSink?.invoke(CallScreenEvents.Hangup) + eventSink?.invoke(CallScreenEvent.Hangup) } } diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index c2c576999c..99f10b46fc 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -14,7 +14,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.features.call.api.CallData -import io.element.android.features.call.impl.ui.CallScreenEvents +import io.element.android.features.call.impl.ui.CallScreenEvent import io.element.android.features.call.impl.ui.CallScreenNavigator import io.element.android.features.call.impl.ui.CallScreenPresenter import io.element.android.features.call.impl.utils.WidgetMessageSerializer @@ -109,7 +109,7 @@ class CallScreenPresenterTest { advanceTimeBy(1.seconds) val initialState = awaitItem() - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + initialState.eventSink(CallScreenEvent.SetupMessageChannels(messageInterceptor)) // And incoming message from the Widget Driver is passed to the WebView widgetDriver.givenIncomingMessage("A message") @@ -143,9 +143,9 @@ class CallScreenPresenterTest { // Give it time to load the URL and WidgetDriver advanceTimeBy(1.seconds) - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + initialState.eventSink(CallScreenEvent.SetupMessageChannels(messageInterceptor)) - initialState.eventSink(CallScreenEvents.Hangup) + initialState.eventSink(CallScreenEvent.Hangup) // Let background coroutines run and the widget drive be received runCurrent() @@ -177,7 +177,7 @@ class CallScreenPresenterTest { // Give it time to load the URL and WidgetDriver advanceTimeBy(1.seconds) - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + initialState.eventSink(CallScreenEvent.SetupMessageChannels(messageInterceptor)) messageInterceptor.givenInterceptedMessage("""{"action":"io.element.close","api":"fromWidget","widgetId":"1","requestId":"1"}""") @@ -212,7 +212,7 @@ class CallScreenPresenterTest { skipItems(2) val initialState = awaitItem() assertThat(initialState.isCallActive).isFalse() - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + initialState.eventSink(CallScreenEvent.SetupMessageChannels(messageInterceptor)) messageInterceptor.givenInterceptedMessage( """ { @@ -249,7 +249,7 @@ class CallScreenPresenterTest { skipItems(2) val initialState = awaitItem() assertThat(initialState.isCallActive).isFalse() - initialState.eventSink(CallScreenEvents.SetupMessageChannels(messageInterceptor)) + initialState.eventSink(CallScreenEvent.SetupMessageChannels(messageInterceptor)) skipItems(2) // Wait for the timeout to trigger From 944d8965f66989f92c2a03653debb82bfd987899 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Apr 2026 17:04:49 +0200 Subject: [PATCH 009/471] `PictureInPictureEvents` -> `PictureInPictureEvent` --- ...ctureEvents.kt => PictureInPictureEvent.kt} | 8 ++++---- .../call/impl/pip/PictureInPicturePresenter.kt | 8 ++++---- .../call/impl/pip/PictureInPictureState.kt | 2 +- .../impl/pip/PictureInPictureStateProvider.kt | 2 +- .../features/call/impl/ui/CallScreenView.kt | 6 +++--- .../call/impl/ui/ElementCallActivity.kt | 6 +++--- .../impl/pip/PictureInPicturePresenterTest.kt | 18 +++++++++--------- 7 files changed, 25 insertions(+), 25 deletions(-) rename features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/{PictureInPictureEvents.kt => PictureInPictureEvent.kt} (75%) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvents.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvent.kt similarity index 75% rename from features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvents.kt rename to features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvent.kt index 9522d44b22..1af13e148e 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvents.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureEvent.kt @@ -10,8 +10,8 @@ package io.element.android.features.call.impl.pip import io.element.android.features.call.impl.utils.PipController -sealed interface PictureInPictureEvents { - data class SetPipController(val pipController: PipController) : PictureInPictureEvents - data object EnterPictureInPicture : PictureInPictureEvents - data class OnPictureInPictureModeChanged(val isInPip: Boolean) : PictureInPictureEvents +sealed interface PictureInPictureEvent { + data class SetPipController(val pipController: PipController) : PictureInPictureEvent + data object EnterPictureInPicture : PictureInPictureEvent + data class OnPictureInPictureModeChanged(val isInPip: Boolean) : PictureInPictureEvent } diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenter.kt index 5125b464dc..2dc16d7f26 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenter.kt @@ -36,17 +36,17 @@ class PictureInPicturePresenter( var isInPictureInPicture by remember { mutableStateOf(false) } var pipController by remember { mutableStateOf(null) } - fun handleEvent(event: PictureInPictureEvents) { + fun handleEvent(event: PictureInPictureEvent) { when (event) { - is PictureInPictureEvents.SetPipController -> { + is PictureInPictureEvent.SetPipController -> { pipController = event.pipController } - PictureInPictureEvents.EnterPictureInPicture -> { + PictureInPictureEvent.EnterPictureInPicture -> { coroutineScope.launch { switchToPip(pipController) } } - is PictureInPictureEvents.OnPictureInPictureModeChanged -> { + is PictureInPictureEvent.OnPictureInPictureModeChanged -> { Timber.tag(loggerTag.value).d("onPictureInPictureModeChanged: ${event.isInPip}") isInPictureInPicture = event.isInPip if (event.isInPip) { diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureState.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureState.kt index b1fef4f28b..108589edb9 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureState.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureState.kt @@ -11,5 +11,5 @@ package io.element.android.features.call.impl.pip data class PictureInPictureState( val supportPip: Boolean, val isInPictureInPicture: Boolean, - val eventSink: (PictureInPictureEvents) -> Unit, + val eventSink: (PictureInPictureEvent) -> Unit, ) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureStateProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureStateProvider.kt index 6324820eec..f4a78294b6 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureStateProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/pip/PictureInPictureStateProvider.kt @@ -11,7 +11,7 @@ package io.element.android.features.call.impl.pip fun aPictureInPictureState( supportPip: Boolean = false, isInPictureInPicture: Boolean = false, - eventSink: (PictureInPictureEvents) -> Unit = {}, + eventSink: (PictureInPictureEvent) -> Unit = {}, ): PictureInPictureState { return PictureInPictureState( supportPip = supportPip, 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 a945f3c844..1c68a62f55 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 @@ -33,7 +33,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.viewinterop.AndroidView import io.element.android.features.call.impl.R -import io.element.android.features.call.impl.pip.PictureInPictureEvents +import io.element.android.features.call.impl.pip.PictureInPictureEvent import io.element.android.features.call.impl.pip.PictureInPictureState import io.element.android.features.call.impl.pip.aPictureInPictureState import io.element.android.features.call.impl.utils.InvalidAudioDeviceReason @@ -66,7 +66,7 @@ internal fun CallScreenView( ) { fun handleBack() { if (pipState.supportPip) { - pipState.eventSink.invoke(PictureInPictureEvents.EnterPictureInPicture) + pipState.eventSink.invoke(PictureInPictureEvent.EnterPictureInPicture) } else { state.eventSink(CallScreenEvent.Hangup) } @@ -132,7 +132,7 @@ internal fun CallScreenView( ) state.eventSink(CallScreenEvent.SetupMessageChannels(interceptor)) val pipController = WebViewPipController(webView) - pipState.eventSink(PictureInPictureEvents.SetPipController(pipController)) + pipState.eventSink(PictureInPictureEvent.SetPipController(pipController)) }, onDestroyWebView = { // Reset audio mode 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 bce882ec2d..367328ed10 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 @@ -38,7 +38,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.call.api.CallData 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 +import io.element.android.features.call.impl.pip.PictureInPictureEvent import io.element.android.features.call.impl.pip.PictureInPicturePresenter import io.element.android.features.call.impl.pip.PictureInPictureState import io.element.android.features.call.impl.pip.PipView @@ -159,7 +159,7 @@ class ElementCallActivity : if (requestPermissionCallback != null) { Timber.tag(loggerTag.value).w("Ignoring onUserLeaveHint event because user is asked to grant permissions") } else { - pipEventSink(PictureInPictureEvents.EnterPictureInPicture) + pipEventSink(PictureInPictureEvent.EnterPictureInPicture) } } addOnUserLeaveHintListener(listener) @@ -169,7 +169,7 @@ class ElementCallActivity : } DisposableEffect(Unit) { val onPictureInPictureModeChangedListener = Consumer { _: PictureInPictureModeChangedInfo -> - pipEventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(isInPictureInPictureMode)) + pipEventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(isInPictureInPictureMode)) if (!isInPictureInPictureMode && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { Timber.tag(loggerTag.value).d("Exiting PiP mode: Hangup the call") eventSink?.invoke(CallScreenEvent.Hangup) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt index c087fa3c35..97aa63947b 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt @@ -58,13 +58,13 @@ class PictureInPicturePresenterTest { }.test { val initialState = awaitItem() assertThat(initialState.isInPictureInPicture).isFalse() - initialState.eventSink(PictureInPictureEvents.EnterPictureInPicture) + initialState.eventSink(PictureInPictureEvent.EnterPictureInPicture) enterPipModeResult.assertions().isCalledOnce() - initialState.eventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(true)) + initialState.eventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(true)) val pipState = awaitItem() assertThat(pipState.isInPictureInPicture).isTrue() // User stops pip - initialState.eventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(false)) + initialState.eventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(false)) val finalState = awaitItem() assertThat(finalState.isInPictureInPicture).isFalse() } @@ -84,8 +84,8 @@ class PictureInPicturePresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(PictureInPictureEvents.SetPipController(FakePipController(canEnterPipResult = { false }))) - initialState.eventSink(PictureInPictureEvents.EnterPictureInPicture) + initialState.eventSink(PictureInPictureEvent.SetPipController(FakePipController(canEnterPipResult = { false }))) + initialState.eventSink(PictureInPictureEvent.EnterPictureInPicture) handUpResult.assertions().isCalledOnce() } } @@ -107,7 +107,7 @@ class PictureInPicturePresenterTest { }.test { val initialState = awaitItem() initialState.eventSink( - PictureInPictureEvents.SetPipController( + PictureInPictureEvent.SetPipController( FakePipController( canEnterPipResult = { true }, enterPipResult = enterPipResult, @@ -115,16 +115,16 @@ class PictureInPicturePresenterTest { ) ) ) - initialState.eventSink(PictureInPictureEvents.EnterPictureInPicture) + initialState.eventSink(PictureInPictureEvent.EnterPictureInPicture) enterPipModeResult.assertions().isCalledOnce() enterPipResult.assertions().isNeverCalled() - initialState.eventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(true)) + initialState.eventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(true)) val pipState = awaitItem() assertThat(pipState.isInPictureInPicture).isTrue() enterPipResult.assertions().isCalledOnce() // User stops pip exitPipResult.assertions().isNeverCalled() - initialState.eventSink(PictureInPictureEvents.OnPictureInPictureModeChanged(false)) + initialState.eventSink(PictureInPictureEvent.OnPictureInPictureModeChanged(false)) val finalState = awaitItem() assertThat(finalState.isInPictureInPicture).isFalse() exitPipResult.assertions().isCalledOnce() From 4bb33fc36a6cb5b7238378b663214169fcaf512d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 27 Apr 2026 17:06:49 +0200 Subject: [PATCH 010/471] Use test extension on presenters. --- .../impl/pip/PictureInPicturePresenterTest.kt | 24 +++++------------- .../call/ui/CallScreenPresenterTest.kt | 25 ++++++------------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt index 97aa63947b..c3d7fdf17b 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/pip/PictureInPicturePresenterTest.kt @@ -8,11 +8,9 @@ package io.element.android.features.call.impl.pip -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.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Test @@ -20,9 +18,7 @@ class PictureInPicturePresenterTest { @Test fun `when pip is not supported, the state value supportPip is false`() = runTest { val presenter = createPictureInPicturePresenter(supportPip = false) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.supportPip).isFalse() } @@ -35,9 +31,7 @@ class PictureInPicturePresenterTest { supportPip = true, pipView = FakePipView(setPipParamsResult = { }), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.supportPip).isTrue() } @@ -53,9 +47,7 @@ class PictureInPicturePresenterTest { enterPipModeResult = enterPipModeResult, ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.isInPictureInPicture).isFalse() initialState.eventSink(PictureInPictureEvent.EnterPictureInPicture) @@ -80,9 +72,7 @@ class PictureInPicturePresenterTest { handUpResult = handUpResult ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink(PictureInPictureEvent.SetPipController(FakePipController(canEnterPipResult = { false }))) initialState.eventSink(PictureInPictureEvent.EnterPictureInPicture) @@ -102,9 +92,7 @@ class PictureInPicturePresenterTest { enterPipModeResult = enterPipModeResult ), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() initialState.eventSink( PictureInPictureEvent.SetPipController( diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt index 99f10b46fc..276e6670f1 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt @@ -39,6 +39,7 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.tests.testutils.WarmUpRule 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.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancelAndJoin @@ -71,9 +72,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker(analyticsLambda), activeCallManager = FakeActiveCallManager(joinedCallResult = joinedCallLambda), ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { // Wait until the URL is loaded advanceTimeBy(1.seconds) skipItems(1) @@ -102,9 +101,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker {}, ) val messageInterceptor = FakeWidgetMessageInterceptor() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { // Give it time to load the URL and WidgetDriver advanceTimeBy(1.seconds) @@ -135,9 +132,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker {}, ) val messageInterceptor = FakeWidgetMessageInterceptor() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() // Give it time to load the URL and WidgetDriver @@ -169,9 +164,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker {}, ) val messageInterceptor = FakeWidgetMessageInterceptor() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() // Give it time to load the URL and WidgetDriver @@ -204,9 +197,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker {}, ) val messageInterceptor = FakeWidgetMessageInterceptor() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { // Give it time to load the URL and WidgetDriver advanceTimeBy(1.seconds) skipItems(2) @@ -241,9 +232,7 @@ class CallScreenPresenterTest { screenTracker = FakeScreenTracker {}, ) val messageInterceptor = FakeWidgetMessageInterceptor() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { // Give it time to load the URL and WidgetDriver advanceTimeBy(1.seconds) skipItems(2) From fff27ff3f1e843d40023892800d01dad47f4fa16 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 27 Apr 2026 16:31:20 +0000 Subject: [PATCH 011/471] Strip formatting from media captions in room summary --- .../impl/DefaultPinnedMessagesBannerFormatter.kt | 16 +++++++++------- .../impl/DefaultRoomLatestEventFormatter.kt | 16 +++++++++------- .../libraries/matrix/ui/messages/ToPlainText.kt | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 14 deletions(-) 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 7878245aca..c8afba3603 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 @@ -77,26 +77,28 @@ class DefaultPinnedMessagesBannerFormatter( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_video) + messageType.toPlainText(permalinkParser).prefixWith(CommonStrings.common_video) } is ImageMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_image) + messageType.toPlainText(permalinkParser).prefixWith(CommonStrings.common_image) } is StickerMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_sticker) + messageType.toPlainText(permalinkParser).prefixWith(CommonStrings.common_sticker) } is LocationMessageType -> { messageType.body.prefixWith(CommonStrings.common_shared_location) } is FileMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_file) + messageType.toPlainText(permalinkParser).prefixWith(CommonStrings.common_file) } is AudioMessageType -> { - messageType.bestDescription.prefixWith(CommonStrings.common_audio) + messageType.toPlainText(permalinkParser).prefixWith(CommonStrings.common_audio) } is VoiceMessageType -> { - // 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)) + messageType + .toPlainText(permalinkParser, "") + .takeIf { it.isNotEmpty() } + ?.prefixWith(sp.getString(CommonStrings.common_voice_message)) ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index 3ecd7819e5..b9b6467c92 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -139,26 +139,28 @@ class DefaultRoomLatestEventFormatter( messageType.toPlainText(permalinkParser) } is VideoMessageType -> { - messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_video)) + messageType.toPlainText(permalinkParser).prefixWith(sp.getString(CommonStrings.common_video)) } is ImageMessageType -> { - messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_image)) + messageType.toPlainText(permalinkParser).prefixWith(sp.getString(CommonStrings.common_image)) } is StickerMessageType -> { - messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_sticker)) + messageType.toPlainText(permalinkParser).prefixWith(sp.getString(CommonStrings.common_sticker)) } is LocationMessageType -> { sp.getString(CommonStrings.common_shared_location) } is FileMessageType -> { - messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_file)) + messageType.toPlainText(permalinkParser).prefixWith(sp.getString(CommonStrings.common_file)) } is AudioMessageType -> { - messageType.bestDescription.prefixWith(sp.getString(CommonStrings.common_audio)) + messageType.toPlainText(permalinkParser).prefixWith(sp.getString(CommonStrings.common_audio)) } is VoiceMessageType -> { - // 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)) + messageType + .toPlainText(permalinkParser, "") + .takeIf { it.isNotEmpty() } + ?.prefixWith(sp.getString(CommonStrings.common_voice_message)) ?: sp.getString(CommonStrings.common_voice_message) } is OtherMessageType -> { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt index d58d12b785..34dc72aa1a 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.ui.messages import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat +import io.element.android.libraries.matrix.api.timeline.item.event.MessageTypeWithAttachment import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import org.jsoup.nodes.Document import org.jsoup.nodes.Element @@ -26,6 +27,19 @@ fun TextMessageType.toPlainText( permalinkParser: PermalinkParser, ) = formatted?.toPlainText(permalinkParser) ?: body +/** + * Converts the HTML string in [MessageTypeWithAttachment.formattedCaption] to a plain text representation by parsing it and removing all formatting. + * If the caption is not formatted or the format is not [MessageFormat.HTML], the [MessageTypeWithAttachment.caption] is returned instead. + * If there is no caption, returns [default]. + */ +fun MessageTypeWithAttachment.toPlainText( + permalinkParser: PermalinkParser, + default: String = filename, +): String { + val plainTextFromFormatted = formattedCaption?.toPlainText(permalinkParser) + return plainTextFromFormatted ?: caption ?: default +} + /** * Converts the HTML string in [FormattedBody.body] to a plain text representation by parsing it and removing all formatting. * If the message is not formatted or the format is not [MessageFormat.HTML] we return `null`. From 5e8db7400369cf0e133db5a6d87934cfd61878a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:16:50 +0200 Subject: [PATCH 012/471] Update dependencyAnalysis to v3.9.0 (#6657) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 53812f1d74..97b1b959e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,7 +51,7 @@ telephoto = "0.19.0" haze = "1.7.2" # Dependency analysis -dependencyAnalysis = "3.7.0" +dependencyAnalysis = "3.9.0" # DI metro = "0.13.2" From 55084422229d824ccb1254f1655632cc91bf3220 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 28 Apr 2026 12:04:57 +0200 Subject: [PATCH 013/471] [Link new device] Implement code confirmation screen. --- .../impl/LinkNewDeviceFlowNode.kt | 22 ++- .../confirmation/CodeConfirmationNode.kt | 47 ++++++ .../confirmation/CodeConfirmationView.kt | 134 ++++++++++++++++++ .../impl/src/main/res/values/localazy.xml | 3 + tools/localazy/config.json | 4 +- 5 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationNode.kt create mode 100644 features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationView.kt diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index 54baee6663..f69abc944b 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -27,6 +27,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint +import io.element.android.features.linknewdevice.impl.screens.confirmation.CodeConfirmationNode import io.element.android.features.linknewdevice.impl.screens.desktop.DesktopNoticeNode import io.element.android.features.linknewdevice.impl.screens.error.ErrorNode import io.element.android.features.linknewdevice.impl.screens.error.ErrorScreenType @@ -107,6 +108,11 @@ class LinkNewDeviceFlowNode( val data: String, ) : NavTarget + @Parcelize + data class CodeConfirmation( + val code: String, + ) : NavTarget + @Parcelize data object MobileEnterNumber : NavTarget @@ -166,7 +172,9 @@ class LinkNewDeviceFlowNode( is LinkDesktopStep.Error -> { navigateToError(linkDesktopStep.errorType) } - is LinkDesktopStep.EstablishingSecureChannel -> Unit + is LinkDesktopStep.EstablishingSecureChannel -> { + backstack.push(NavTarget.CodeConfirmation(linkDesktopStep.checkCodeString)) + } is LinkDesktopStep.InvalidQrCode -> { // This error will be handled by the ScanQrCodeNode } @@ -250,6 +258,18 @@ class LinkNewDeviceFlowNode( } createNode(buildContext, listOf(callback)) } + is NavTarget.CodeConfirmation -> { + val callback = object : CodeConfirmationNode.Callback { + override fun onCancel() { + // Push error + backstack.push(NavTarget.Error(ErrorScreenType.Cancelled)) + } + } + val inputs = CodeConfirmationNode.Inputs( + code = navTarget.code, + ) + createNode(buildContext, listOf(inputs, callback)) + } is NavTarget.MobileShowQrCode -> { val callback = object : ShowQrCodeNode.Callback { override fun navigateBack() { diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationNode.kt new file mode 100644 index 0000000000..a8db4d2d75 --- /dev/null +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationNode.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.linknewdevice.impl.screens.confirmation + +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 dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedInject +import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.SessionScope + +@ContributesNode(SessionScope::class) +@AssistedInject +class CodeConfirmationNode( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, +) : Node(buildContext = buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onCancel() + } + + data class Inputs( + val code: String, + ) : NodeInputs + + private val callback: Callback = callback() + private val input = inputs() + + @Composable + override fun View(modifier: Modifier) { + CodeConfirmationView( + code = input.code, + onCancel = callback::onCancel, + ) + } +} diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationView.kt new file mode 100644 index 0000000000..d981574f86 --- /dev/null +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/confirmation/CodeConfirmationView.kt @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.linknewdevice.impl.screens.confirmation + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Spacer +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.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +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.linknewdevice.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.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.OutlinedButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun CodeConfirmationView( + code: String, + onCancel: () -> Unit, + modifier: Modifier = Modifier, +) { + BackHandler(onBack = onCancel) + FlowStepPage( + modifier = modifier, + iconStyle = BigIcon.Style.Default(CompoundIcons.Computer()), + title = stringResource(R.string.screen_qr_code_login_device_code_title), + subTitle = stringResource(R.string.screen_qr_code_login_device_code_subtitle), + content = { Content(code = code) }, + buttons = { Buttons(onCancel = onCancel) } + ) +} + +@Composable +private fun Content(code: String) { + Column( + modifier = Modifier.padding(top = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Digits(code = code) + Spacer(modifier = Modifier.height(32.dp)) + WaitingForOtherDevice() + } +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +private fun Digits(code: String) { + FlowRow( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + code.forEach { + Text( + modifier = Modifier + .padding(horizontal = 6.dp, vertical = 4.dp) + .clip(RoundedCornerShape(4.dp)) + .background(ElementTheme.colors.bgActionSecondaryPressed) + .padding(horizontal = 16.dp, vertical = 17.dp), + text = it.toString() + ) + } + } +} + +@Composable +private fun WaitingForOtherDevice() { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + CircularProgressIndicator( + modifier = Modifier + .size(20.dp) + .padding(2.dp), + strokeWidth = 2.dp, + ) + Text( + text = stringResource(R.string.screen_qr_code_login_verify_code_loading), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + textAlign = TextAlign.Center, + ) + } +} + +@Composable +private fun Buttons( + onCancel: () -> Unit, +) { + Column(modifier = Modifier.fillMaxWidth()) { + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_cancel), + onClick = onCancel, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun CodeConfirmationViewPreview() { + ElementPreview { + CodeConfirmationView( + code = "67", + onCancel = {}, + ) + } +} diff --git a/features/linknewdevice/impl/src/main/res/values/localazy.xml b/features/linknewdevice/impl/src/main/res/values/localazy.xml index 321b168751..6ffcce227a 100644 --- a/features/linknewdevice/impl/src/main/res/values/localazy.xml +++ b/features/linknewdevice/impl/src/main/res/values/localazy.xml @@ -34,6 +34,8 @@ "If you encounter the same problem, try a different wifi network or use your mobile data instead of wifi" "If that doesn’t work, sign in manually" "Connection not secure" + "You’ll be asked to enter the two digits shown on this device." + "Enter the number below on your other device" "The sign in was cancelled on the other device." "Sign in request cancelled" "The sign in was declined on the other device." @@ -54,4 +56,5 @@ Try signing in manually, or scan the QR code with another device." "You need to give permission for %1$s to use your device’s camera in order to continue." "Allow camera access to scan the QR code" "An unexpected error occurred. Please try again." + "Waiting for your other device" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index b38268e5f7..03ada90c4b 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -163,7 +163,9 @@ "screen_qr_code_login_connection_note_secure_state.*", "screen_qr_code_login_unknown_error_description", "screen_qr_code_login_invalid_scan_state_.*", - "screen_qr_code_login_no_camera_permission_state_.*" + "screen_qr_code_login_no_camera_permission_state_.*", + "screen_qr_code_login_device_code_.*", + "screen_qr_code_login_verify_code_loading" ] }, { From 5c9d8d917c6721a21395d6eadef3cbb26d97da79 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 28 Apr 2026 11:22:25 +0100 Subject: [PATCH 014/471] Update error mappings for Link new device --- .../impl/LinkNewDeviceFlowNode.kt | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index 54baee6663..ebaf1cda4e 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -183,20 +183,15 @@ class LinkNewDeviceFlowNode( private fun navigateToError(errorType: ErrorType) { // Map the error to an error screen - // TODO Update this mapping val error = when (errorType) { - is ErrorType.DeviceIdAlreadyInUse -> ErrorScreenType.UnknownError - is ErrorType.InvalidCheckCode -> ErrorScreenType.InsecureChannelDetected - is ErrorType.MissingSecretsBackup -> ErrorScreenType.UnknownError - is ErrorType.NotFound -> ErrorScreenType.Expired - is ErrorType.DeviceNotFound -> ErrorScreenType.UnknownError - is ErrorType.Unknown -> ErrorScreenType.UnknownError - is ErrorType.UnsupportedProtocol -> ErrorScreenType.UnknownError - is ErrorType.Cancelled -> ErrorScreenType.UnknownError + is ErrorType.InvalidCheckCode -> ErrorScreenType.Mismatch2Digits + is ErrorType.UnsupportedProtocol -> ErrorScreenType.ProtocolNotSupported + is ErrorType.Cancelled -> ErrorScreenType.Cancelled is ErrorType.ConnectionInsecure -> ErrorScreenType.InsecureChannelDetected - is ErrorType.Expired -> ErrorScreenType.Expired - is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError - is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError + is ErrorType.Expired, is ErrorType.NotFound, is ErrorType.DeviceNotFound -> ErrorScreenType.Expired + is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError // TODO: should show other_device_already_signed_in screen with checkmark when available + is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError // TODO: check if we expect to hit this here or if it should be caught earlier on + is ErrorType.MissingSecretsBackup, is ErrorType.DeviceIdAlreadyInUse, is ErrorType.Unknown -> ErrorScreenType.UnknownError } // It is OK to push on backstack, since when user leaves the error screen, a new root will be set, // or the whole flow will be popped. From cd61c6b3f072da39eef2a2731f47a954edb49f0d Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 28 Apr 2026 11:10:56 +0000 Subject: [PATCH 015/471] Update screenshots --- ...impl.screens.confirmation_CodeConfirmationView_Day_0_en.png | 3 +++ ...pl.screens.confirmation_CodeConfirmationView_Night_0_en.png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png new file mode 100644 index 0000000000..a9e31653f6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8dab1dc964cea9a76dc7130d8ac2bfe5d3c866fd6d8d969101eb50e828775d3 +size 31915 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png new file mode 100644 index 0000000000..43c472ebb1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec67e3fef25c57331cb2425f8fc46a733e16a98c9945d0a4e5b950843c42fa34 +size 31087 From 90ed38574510137f86dc93bcd518a0f07ff952d8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 28 Apr 2026 13:17:57 +0200 Subject: [PATCH 016/471] Fix record screenshots action permissions (#6679) --- .github/workflows/recordScreenshots.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index 0f4c8ee581..4b70cffe61 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -17,6 +17,7 @@ jobs: permissions: # Need write permissions on PRs to remove the label "Record-Screenshots" pull-requests: write + contents: write name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }} runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots' From 997227b020dc16471dce69086b6ceb369bdc53c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:49:37 +0200 Subject: [PATCH 017/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27 (#6666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27 * Fix breaking API changes: - OIDC components are now prefixed `OAuth`. - `Room.startLiveLocationShare` now returns the event id of the beacon state event if it succeeds. - `RoomInfo` now contains an `activeServiceMembersCount` property. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- gradle/libs.versions.toml | 2 +- .../libraries/matrix/api/room/JoinedRoom.kt | 4 ++-- .../matrix/impl/RustMatrixClientFactory.kt | 2 +- .../matrix/impl/auth/AuthenticationException.kt | 14 +++++++------- .../matrix/impl/auth/HomeserverDetails.kt | 2 +- .../matrix/impl/auth/OidcConfigurationProvider.kt | 4 ++-- .../libraries/matrix/impl/auth/OidcPrompt.kt | 2 +- .../RustHomeServerLoginCompatibilityChecker.kt | 4 ++-- .../impl/auth/RustMatrixAuthenticationService.kt | 10 +++++----- .../matrix/impl/auth/qrlogin/QrErrorMapper.kt | 2 +- .../impl/encryption/RustIdentityResetHandle.kt | 2 +- .../libraries/matrix/impl/mapper/Session.kt | 2 +- .../libraries/matrix/impl/room/JoinedRustRoom.kt | 6 +++--- .../impl/room/location/LiveLocationSharesFlow.kt | 6 +++--- .../auth/AuthenticationExceptionMappingTest.kt | 12 ++++++------ .../matrix/impl/auth/qrlogin/QrErrorMapperTest.kt | 2 +- .../impl/fixtures/factories/NotificationItem.kt | 5 +++++ .../matrix/impl/fixtures/factories/RoomInfo.kt | 2 ++ .../matrix/impl/fixtures/factories/Session.kt | 2 +- .../fakes/FakeFfiHomeserverLoginDetails.kt | 2 +- .../libraries/matrix/test/room/FakeJoinedRoom.kt | 5 +++-- 21 files changed, 50 insertions(+), 42 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97b1b959e8..b942901e19 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.04.21" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.04.27" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt index 32a6f2e409..f3fefab9a8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt @@ -196,9 +196,9 @@ interface JoinedRoom : BaseRoom { /** * Start sharing live location in this room. * @param durationMillis How long to share location (in milliseconds). - * @return Result indicating success or failure. + * @return Result containing the [EventId] of the beacon state event on success or an error on failure. */ - suspend fun startLiveLocationShare(durationMillis: Long): Result + suspend fun startLiveLocationShare(durationMillis: Long): Result /** * Stop sharing live location in this room. 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 933298bc6c..e5bae3bb9d 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 @@ -214,5 +214,5 @@ fun SessionData.toSession() = Session( deviceId = deviceId, homeserverUrl = homeserverUrl, slidingSyncVersion = SlidingSyncVersion.NATIVE, - oidcData = oidcData, + oauthData = oidcData, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index ebe0c5e4e8..2a151057b3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.impl.auth import io.element.android.libraries.matrix.api.auth.AuthenticationException import org.matrix.rustcomponents.sdk.ClientBuildException -import org.matrix.rustcomponents.sdk.OidcException +import org.matrix.rustcomponents.sdk.OAuthException fun Throwable.mapAuthenticationException(): AuthenticationException { return when (this) { @@ -29,12 +29,12 @@ fun Throwable.mapAuthenticationException(): AuthenticationException { is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message) is ClientBuildException.EventCache -> AuthenticationException.Generic(message) } - is OidcException -> when (this) { - is OidcException.Generic -> AuthenticationException.Oidc(message) - is OidcException.CallbackUrlInvalid -> AuthenticationException.Oidc(message) - is OidcException.Cancelled -> AuthenticationException.Oidc(message) - is OidcException.MetadataInvalid -> AuthenticationException.Oidc(message) - is OidcException.NotSupported -> AuthenticationException.Oidc(message) + is OAuthException -> when (this) { + is OAuthException.Generic -> AuthenticationException.Oidc(message) + is OAuthException.CallbackUrlInvalid -> AuthenticationException.Oidc(message) + is OAuthException.Cancelled -> AuthenticationException.Oidc(message) + is OAuthException.MetadataInvalid -> AuthenticationException.Oidc(message) + is OAuthException.NotSupported -> AuthenticationException.Oidc(message) } else -> AuthenticationException.Generic(message) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt index acf96d69d4..acf3a5a55b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt @@ -15,6 +15,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use { MatrixHomeServerDetails( url = url(), supportsPasswordLogin = supportsPasswordLogin(), - supportsOidcLogin = supportsOidcLogin(), + supportsOidcLogin = supportsOauthLogin(), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt index 6f9dd67b12..033b613dd8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt @@ -12,14 +12,14 @@ import dev.zacsweers.metro.Inject import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.auth.OidcConfig import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider -import org.matrix.rustcomponents.sdk.OidcConfiguration +import org.matrix.rustcomponents.sdk.OAuthConfiguration @Inject class OidcConfigurationProvider( private val buildMeta: BuildMeta, private val oidcRedirectUrlProvider: OidcRedirectUrlProvider, ) { - fun get(): OidcConfiguration = OidcConfiguration( + fun get(): OAuthConfiguration = OAuthConfiguration( clientName = buildMeta.applicationName, redirectUri = oidcRedirectUrlProvider.provide(), clientUri = OidcConfig.CLIENT_URI, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt index e21d8d94c6..a23a6e5041 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt @@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.impl.auth import io.element.android.libraries.matrix.api.auth.OidcPrompt -import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt +import org.matrix.rustcomponents.sdk.OAuthPrompt as RustOidcPrompt internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt { return when (this) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt index 0603fddec4..4f59906023 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt @@ -31,8 +31,8 @@ class RustHomeServerLoginCompatibilityChecker( it.homeserverLoginDetails() } .use { - Timber.d("Homeserver $url | OIDC: ${it.supportsOidcLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}") - it.supportsOidcLogin() || it.supportsPasswordLogin() + Timber.d("Homeserver $url | OIDC: ${it.supportsOauthLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}") + it.supportsOauthLogin() || it.supportsPasswordLogin() } } } 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 9fe7c7cd1f..d186f06209 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 @@ -260,8 +260,8 @@ class RustMatrixAuthenticationService( return withContext(coroutineDispatchers.io) { runCatchingExceptions { val client = currentClient ?: error("You need to call `setHomeserver()` first") - val oAuthAuthorizationData = client.urlForOidc( - oidcConfiguration = oidcConfigurationProvider.get(), + val oAuthAuthorizationData = client.urlForOauth( + oauthConfiguration = oidcConfigurationProvider.get(), prompt = prompt.toRustPrompt(), loginHint = loginHint, // If we want to restore a previous session for which we have encryption keys, we can pass the deviceId here. At the moment, we don't @@ -282,7 +282,7 @@ class RustMatrixAuthenticationService( return withContext(coroutineDispatchers.io) { runCatchingExceptions { pendingOAuthAuthorizationData?.use { - currentClient?.abortOidcAuth(it) + currentClient?.abortOauthAuth(it) } pendingOAuthAuthorizationData = null }.mapFailure { failure -> @@ -304,7 +304,7 @@ class RustMatrixAuthenticationService( runCatchingExceptions { val client = currentClient ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") - client.loginWithOidcCallback( + client.loginWithOauthCallback( callbackUrl = callbackUrl, ) // Free the pending data since we won't use it to abort the flow anymore @@ -368,7 +368,7 @@ class RustMatrixAuthenticationService( qrCodeData = sdkQrCodeLoginData, ) client.newLoginWithQrCodeHandler( - oidcConfiguration = oidcConfiguration, + oauthConfiguration = oidcConfiguration, ).use { it.scan( qrCodeData = qrCodeData.rustQrCodeData, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt index ae56cb10fa..1c4e300e91 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt @@ -42,7 +42,7 @@ object QrErrorMapper { is RustHumanQrLoginException.OtherDeviceNotSignedIn -> QrLoginException.OtherDeviceNotSignedIn is RustHumanQrLoginException.LinkingNotSupported -> QrLoginException.LinkingNotSupported is RustHumanQrLoginException.Unknown -> QrLoginException.Unknown - is RustHumanQrLoginException.OidcMetadataInvalid -> QrLoginException.OidcMetadataInvalid + is RustHumanQrLoginException.OAuthMetadataInvalid -> QrLoginException.OidcMetadataInvalid is RustHumanQrLoginException.SlidingSyncNotAvailable -> QrLoginException.SlidingSyncNotAvailable is RustHumanQrLoginException.CheckCodeAlreadySent -> QrLoginException.CheckCodeAlreadySent is RustHumanQrLoginException.CheckCodeCannotBeSent -> QrLoginException.CheckCodeCannotBeSent diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt index 4813ec1cc3..8a49d1b11b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt @@ -25,7 +25,7 @@ object RustIdentityResetHandleFactory { return runCatchingExceptions { identityResetHandle?.let { when (val authType = identityResetHandle.authType()) { - is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) + is CrossSigningResetAuthType.OAuth -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) // User interactive authentication (user + password) CrossSigningResetAuthType.Uiaa -> RustPasswordIdentityResetHandle(userId, identityResetHandle) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt index 3199ebf71a..bfb200a994 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt @@ -27,7 +27,7 @@ internal fun Session.toSessionData( accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = homeserverUrl ?: this.homeserverUrl, - oidcData = oidcData, + oidcData = oauthData, loginTimestamp = Date(), isTokenValid = isTokenValid, loginType = loginType, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index 6507cf38c8..87ef0815ce 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -516,10 +516,10 @@ class JoinedRustRoom( return innerRoom.liveLocationSharesFlow().timedByExpiry(systemClock::epochMillis) } - override suspend fun startLiveLocationShare(durationMillis: Long): Result = withContext(roomDispatcher) { + override suspend fun startLiveLocationShare(durationMillis: Long): Result = withContext(roomDispatcher) { runCatchingExceptions { innerRoom.startLiveLocationShare(durationMillis.toULong()) - } + }.map(::EventId) } override suspend fun stopLiveLocationShare(): Result = withContext(roomDispatcher) { @@ -538,7 +538,7 @@ class JoinedRustRoom( override fun destroy() { baseRoom.destroy() - liveInnerTimeline.destroy() + liveTimeline.close() threadsListService.destroy() Timber.d("Room $roomId destroyed") } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt index bae406a137..1a341d0dc2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt @@ -16,8 +16,8 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow -import org.matrix.rustcomponents.sdk.LiveLocationShareListener import org.matrix.rustcomponents.sdk.LiveLocationShareUpdate +import org.matrix.rustcomponents.sdk.LiveLocationsListener import org.matrix.rustcomponents.sdk.RoomInterface import org.matrix.rustcomponents.sdk.LiveLocationShare as RustLiveLocationShare @@ -41,9 +41,9 @@ fun RoomInterface.liveLocationSharesFlow(): Flow> { } } return callbackFlow { - val liveLocationShares = liveLocationShares() + val liveLocationShares = liveLocationsObserver() val shares: MutableList = ArrayList() - val taskHandle = liveLocationShares.subscribe(object : LiveLocationShareListener { + val taskHandle = liveLocationShares.subscribe(object : LiveLocationsListener { override fun onUpdate(updates: List) { for (update in updates) { shares.applyUpdate(update) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt index 8449d9a6c3..7cfd3392a1 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt @@ -13,7 +13,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.auth.AuthenticationException import org.junit.Test import org.matrix.rustcomponents.sdk.ClientBuildException -import org.matrix.rustcomponents.sdk.OidcException +import org.matrix.rustcomponents.sdk.OAuthException class AuthenticationExceptionMappingTest { @Test @@ -65,15 +65,15 @@ class AuthenticationExceptionMappingTest { @Test fun `mapping Oidc exceptions map to the Oidc Kotlin`() { - assertThat(OidcException.Generic("Generic").mapAuthenticationException()) + assertThat(OAuthException.Generic("Generic").mapAuthenticationException()) .isException("Generic") - assertThat(OidcException.CallbackUrlInvalid("CallbackUrlInvalid").mapAuthenticationException()) + assertThat(OAuthException.CallbackUrlInvalid("CallbackUrlInvalid").mapAuthenticationException()) .isException("CallbackUrlInvalid") - assertThat(OidcException.Cancelled("Cancelled").mapAuthenticationException()) + assertThat(OAuthException.Cancelled("Cancelled").mapAuthenticationException()) .isException("Cancelled") - assertThat(OidcException.MetadataInvalid("MetadataInvalid").mapAuthenticationException()) + assertThat(OAuthException.MetadataInvalid("MetadataInvalid").mapAuthenticationException()) .isException("MetadataInvalid") - assertThat(OidcException.NotSupported("NotSupported").mapAuthenticationException()) + assertThat(OAuthException.NotSupported("NotSupported").mapAuthenticationException()) .isException("NotSupported") } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt index 0ef20c82a6..c20a77ace4 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt @@ -32,7 +32,7 @@ class QrErrorMapperTest { assertThat(QrErrorMapper.map(RustHumanQrLoginException.OtherDeviceNotSignedIn())).isEqualTo(QrLoginException.OtherDeviceNotSignedIn) assertThat(QrErrorMapper.map(RustHumanQrLoginException.LinkingNotSupported())).isEqualTo(QrLoginException.LinkingNotSupported) assertThat(QrErrorMapper.map(RustHumanQrLoginException.Unknown())).isEqualTo(QrLoginException.Unknown) - assertThat(QrErrorMapper.map(RustHumanQrLoginException.OidcMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid) + assertThat(QrErrorMapper.map(RustHumanQrLoginException.OAuthMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid) assertThat(QrErrorMapper.map(RustHumanQrLoginException.SlidingSyncNotAvailable())).isEqualTo(QrLoginException.SlidingSyncNotAvailable) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt index 4db2db7107..82984c480d 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.fixtures.factories import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEvent import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_USER_NAME @@ -68,6 +69,8 @@ internal fun aRustNotificationRoomInfo( isDirect: Boolean = false, joinRule: JoinRule? = null, isSpace: Boolean = false, + serviceMembers: List = emptyList(), + activeServiceMemberCount: Int = 0, ) = NotificationRoomInfo( displayName = displayName, avatarUrl = avatarUrl, @@ -78,6 +81,8 @@ internal fun aRustNotificationRoomInfo( isDirect = isDirect, joinRule = joinRule, isSpace = isSpace, + serviceMembers = serviceMembers.map { it.value }, + activeServiceMembersCount = activeServiceMemberCount.toULong(), ) internal fun aRustNotificationEventTimeline( diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt index 1b0cc12461..491614a7fc 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt @@ -62,6 +62,7 @@ internal fun aRustRoomInfo( serviceMembers: List = emptyList(), isLowPriority: Boolean = false, activeRoomCallConsensusIntent: RtcCallIntentConsensus = RtcCallIntentConsensus.None, + activeServiceMembersCount: Int = 0, ) = RoomInfo( id = id, displayName = displayName, @@ -101,4 +102,5 @@ internal fun aRustRoomInfo( serviceMembers = serviceMembers, isLowPriority = isLowPriority, activeRoomCallConsensusIntent = activeRoomCallConsensusIntent, + activeServiceMembersCount = activeServiceMembersCount.toULong(), ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/Session.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/Session.kt index 4671c457b0..af7f44597c 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/Session.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/Session.kt @@ -24,6 +24,6 @@ internal fun aRustSession( userId = A_USER_ID.value, deviceId = A_DEVICE_ID.value, homeserverUrl = A_HOMESERVER_URL, - oidcData = null, + oauthData = null, slidingSyncVersion = proxy, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt index ade3a2328f..65e24c2494 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt @@ -18,7 +18,7 @@ class FakeFfiHomeserverLoginDetails( private val supportsSsoLogin: Boolean = false, ) : HomeserverLoginDetails(NoHandle) { override fun url(): String = url - override fun supportsOidcLogin(): Boolean = supportsOidcLogin + override fun supportsOauthLogin(): Boolean = supportsOidcLogin override fun supportsPasswordLogin(): Boolean = supportsPasswordLogin override fun supportsSsoLogin(): Boolean = supportsSsoLogin } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt index 84497b38de..d1cd340641 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings +import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.threads.FakeThreadsListService import io.element.android.libraries.matrix.test.timeline.FakeTimeline @@ -238,8 +239,8 @@ class FakeJoinedRoom( return liveLocationSharesFlow } - override suspend fun startLiveLocationShare(durationMillis: Long): Result = simulateLongTask { - startLiveLocationShareResult(durationMillis) + override suspend fun startLiveLocationShare(durationMillis: Long): Result = simulateLongTask { + startLiveLocationShareResult(durationMillis).map { AN_EVENT_ID } } override suspend fun stopLiveLocationShare(): Result = simulateLongTask { From 723b7486bcd8742f4c1b1d9aa1cff003adce3f07 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 28 Apr 2026 15:19:03 +0100 Subject: [PATCH 018/471] Improve detection of completion for Link new device flow The SDK emits a Done progress once complete, but our listener might have been deallocated before receiving the done. --- .../features/linknewdevice/impl/LinkNewDeviceFlowNode.kt | 5 +---- .../matrix/impl/linknewdevice/RustLinkDesktopHandler.kt | 2 ++ .../matrix/impl/linknewdevice/RustLinkMobileHandler.kt | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index 54baee6663..c48678f96c 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -145,10 +145,7 @@ class LinkNewDeviceFlowNode( LinkMobileStep.Starting -> { // This step is not received at the moment, so do nothing } - LinkMobileStep.SyncingSecrets -> { - // LinkMobileStep.Done is not received at the moment, so consider that the flow is done here - callback.onDone() - } + LinkMobileStep.SyncingSecrets -> Unit is LinkMobileStep.WaitingForAuth -> { navigateToBrowser(linkMobileStep.verificationUri) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt index 211bdc3d4e..83e657eb07 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandler.kt @@ -54,6 +54,8 @@ class RustLinkDesktopHandler( } } ) + // We emit Done in case the progress listener was deallocated before scan() sent the Done + _linkDesktopStep.emit(LinkDesktopStep.Done) } catch (e: QrCodeDecodeException) { Timber.tag(tag.value).w(e, "Invalid QR code scanned") _linkDesktopStep.emit( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt index 0189987d96..6d212d4784 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt @@ -49,6 +49,8 @@ class RustLinkMobileHandler( } } ) + // We emit Done in case the progress listener was deallocated before generate() sent the Done + _linkMobileStep.emit(LinkMobileStep.Done) } catch (e: HumanQrGrantLoginException) { Timber.tag(tag.value).w(e, "Error during QR login grant") _linkMobileStep.emit(LinkMobileStep.Error(e.map())) From fd69bbc57ada5bb409213f34c9f981b19d7e88f8 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 28 Apr 2026 15:42:24 +0100 Subject: [PATCH 019/471] Delint --- .../features/linknewdevice/impl/LinkNewDeviceFlowNode.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index ebaf1cda4e..4b3076bca9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -189,7 +189,9 @@ class LinkNewDeviceFlowNode( is ErrorType.Cancelled -> ErrorScreenType.Cancelled is ErrorType.ConnectionInsecure -> ErrorScreenType.InsecureChannelDetected is ErrorType.Expired, is ErrorType.NotFound, is ErrorType.DeviceNotFound -> ErrorScreenType.Expired - is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError // TODO: should show other_device_already_signed_in screen with checkmark when available + // TODO: we should show other_device_already_signed_in screen with checkmark when available + // See: https://github.com/element-hq/element-x-android/issues/6678 + is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError // TODO: check if we expect to hit this here or if it should be caught earlier on is ErrorType.MissingSecretsBackup, is ErrorType.DeviceIdAlreadyInUse, is ErrorType.Unknown -> ErrorScreenType.UnknownError } From 1d03cbfc062bd6a22d0073075f39e1d8664fb681 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 10:34:50 +0200 Subject: [PATCH 020/471] Fix quality issues and formatting --- .../linknewdevice/impl/LinkNewDeviceFlowNode.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index dc17cfff81..2d8fe71949 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -196,12 +196,17 @@ class LinkNewDeviceFlowNode( is ErrorType.UnsupportedProtocol -> ErrorScreenType.ProtocolNotSupported is ErrorType.Cancelled -> ErrorScreenType.Cancelled is ErrorType.ConnectionInsecure -> ErrorScreenType.InsecureChannelDetected - is ErrorType.Expired, is ErrorType.NotFound, is ErrorType.DeviceNotFound -> ErrorScreenType.Expired - // TODO: we should show other_device_already_signed_in screen with checkmark when available + is ErrorType.Expired, + is ErrorType.NotFound, + is ErrorType.DeviceNotFound -> ErrorScreenType.Expired + // TODO we should show other_device_already_signed_in screen with checkmark when available // See: https://github.com/element-hq/element-x-android/issues/6678 is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError - is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError // TODO: check if we expect to hit this here or if it should be caught earlier on - is ErrorType.MissingSecretsBackup, is ErrorType.DeviceIdAlreadyInUse, is ErrorType.Unknown -> ErrorScreenType.UnknownError + // TODO check if we expect to hit this here or if it should be caught earlier on + is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError + is ErrorType.MissingSecretsBackup, + is ErrorType.DeviceIdAlreadyInUse, + is ErrorType.Unknown -> ErrorScreenType.UnknownError } // It is OK to push on backstack, since when user leaves the error screen, a new root will be set, // or the whole flow will be popped. From 083fc5c5fb0a4d3bab9c2f36423d59b41b6f6185 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 11:11:43 +0200 Subject: [PATCH 021/471] [Link new device] Add missing screen for the error case `OtherDeviceAlreadySignedIn` Closes #6678 --- .../impl/LinkNewDeviceFlowNode.kt | 4 +-- .../impl/screens/error/ErrorScreenType.kt | 3 ++ .../screens/error/ErrorScreenTypeProvider.kt | 1 + .../impl/screens/error/ErrorView.kt | 32 ++++++++++++++++--- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index 2d8fe71949..4ccdb6b387 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -199,9 +199,7 @@ class LinkNewDeviceFlowNode( is ErrorType.Expired, is ErrorType.NotFound, is ErrorType.DeviceNotFound -> ErrorScreenType.Expired - // TODO we should show other_device_already_signed_in screen with checkmark when available - // See: https://github.com/element-hq/element-x-android/issues/6678 - is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.UnknownError + is ErrorType.OtherDeviceAlreadySignedIn -> ErrorScreenType.OtherDeviceAlreadySignedIn // TODO check if we expect to hit this here or if it should be caught earlier on is ErrorType.UnsupportedQrCodeType -> ErrorScreenType.UnknownError is ErrorType.MissingSecretsBackup, diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenType.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenType.kt index b92a19ef8a..ad8cc276c5 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenType.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenType.kt @@ -20,6 +20,9 @@ sealed interface ErrorScreenType : NodeInputs, Parcelable { @Parcelize data object Expired : ErrorScreenType + @Parcelize + data object OtherDeviceAlreadySignedIn : ErrorScreenType + @Parcelize data object Mismatch2Digits : ErrorScreenType diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt index 7fd699101b..5946eb9ab2 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorScreenTypeProvider.kt @@ -19,5 +19,6 @@ class ErrorScreenTypeProvider : PreviewParameterProvider { ErrorScreenType.InsecureChannelDetected, ErrorScreenType.SlidingSyncNotAvailable, ErrorScreenType.UnknownError, + ErrorScreenType.OtherDeviceAlreadySignedIn, ) } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt index 9f67e8bc17..4db2aa9ad5 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt @@ -47,17 +47,26 @@ fun ErrorView( ) { val appName = LocalBuildMeta.current.applicationName BackHandler(onBack = onCancel) + val iconStyle = when (errorScreenType) { + ErrorScreenType.OtherDeviceAlreadySignedIn -> BigIcon.Style.SuccessSolid + else -> BigIcon.Style.AlertSolid + } FlowStepPage( modifier = modifier, - iconStyle = BigIcon.Style.AlertSolid, + iconStyle = iconStyle, title = titleText(errorScreenType, appName), subTitle = subtitleText(errorScreenType, appName), content = { Content(errorScreenType) }, buttons = { - Buttons( - onRetry = onRetry, - onCancel = onCancel, - ) + when (errorScreenType) { + ErrorScreenType.OtherDeviceAlreadySignedIn -> DoneButton( + onDone = onCancel, + ) + else -> Buttons( + onRetry = onRetry, + onCancel = onCancel, + ) + } }, ) } @@ -72,6 +81,7 @@ private fun titleText(errorScreenType: ErrorScreenType, appName: String) = when ErrorScreenType.Mismatch2Digits -> stringResource(id = R.string.screen_link_new_device_wrong_number_title) ErrorScreenType.SlidingSyncNotAvailable -> stringResource(id = R.string.screen_qr_code_login_error_sliding_sync_not_supported_title, appName) is ErrorScreenType.UnknownError -> stringResource(CommonStrings.common_something_went_wrong) + ErrorScreenType.OtherDeviceAlreadySignedIn -> stringResource(R.string.screen_qr_code_login_error_device_already_signed_in_title) } @Composable @@ -84,6 +94,7 @@ private fun subtitleText(errorScreenType: ErrorScreenType, appName: String) = wh ErrorScreenType.InsecureChannelDetected -> stringResource(id = R.string.screen_qr_code_login_connection_note_secure_state_description) ErrorScreenType.SlidingSyncNotAvailable -> stringResource(id = R.string.screen_qr_code_login_error_sliding_sync_not_supported_subtitle, appName) is ErrorScreenType.UnknownError -> stringResource(R.string.screen_qr_code_login_unknown_error_description) + ErrorScreenType.OtherDeviceAlreadySignedIn -> stringResource(R.string.screen_qr_code_login_error_device_already_signed_in_subtitle) } @Composable @@ -124,6 +135,17 @@ private fun Content(errorScreenType: ErrorScreenType) { } } +@Composable +private fun DoneButton( + onDone: () -> Unit, +) { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_done), + onClick = onDone, + ) +} + @Composable private fun Buttons( onRetry: () -> Unit, From c750fd102f5708718d2e0c1d595c191d39fb56e5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 11:12:17 +0200 Subject: [PATCH 022/471] Increase title and subtitle vertical padding on FlowStepPage. --- .../android/libraries/designsystem/atomic/pages/FlowStepPage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt index b62f634ece..a29e1b743c 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/FlowStepPage.kt @@ -71,7 +71,7 @@ fun FlowStepPage( }, header = { IconTitleSubtitleMolecule( - modifier = Modifier.padding(bottom = 16.dp), + modifier = Modifier.padding(bottom = 16.dp, start = 8.dp, end = 8.dp), title = title, subTitle = subTitle, iconStyle = iconStyle, From 367995303dd4fc983ec11fd98e75b661fe89d8e4 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 29 Apr 2026 11:41:47 +0200 Subject: [PATCH 023/471] Rename `OIDC` components and variables to `OAuth` (#6686) * Rename `OIDC` components and variables to `OAuth`. This matches the new behavior in the SDK. --- app/build.gradle.kts | 8 +- app/src/main/AndroidManifest.xml | 2 +- ....kt => DefaultOAuthRedirectUrlProvider.kt} | 6 +- ...=> DefaultOAuthRedirectUrlProviderTest.kt} | 4 +- appnav/build.gradle.kts | 4 +- .../io/element/android/appnav/RootFlowNode.kt | 12 +-- .../android/appnav/intent/IntentResolver.kt | 14 +-- .../appnav/intent/IntentResolverTest.kt | 32 +++---- .../appnav/loggedin/LoggedInPresenterTest.kt | 2 +- docs/{oidc.md => oauth.md} | 8 +- features/linknewdevice/impl/build.gradle.kts | 4 +- features/login/impl/build.gradle.kts | 4 +- .../features/login/impl/LoginFlowNode.kt | 30 +++---- .../login/impl/error/ChangeServerError.kt | 2 +- .../features/login/impl/login/LoginHelper.kt | 38 ++++---- .../features/login/impl/login/LoginMode.kt | 4 +- .../login/impl/login/LoginModeView.kt | 8 +- .../login/impl/qrcode/QrCodeLoginFlowNode.kt | 4 +- .../ChooseAccountProviderNode.kt | 6 +- .../ChooseAccountProviderView.kt | 8 +- .../impl/screens/classic/ClassicFlowNode.kt | 8 +- .../loginwithclassic/LoginWithClassicNode.kt | 6 +- .../loginwithclassic/LoginWithClassicView.kt | 8 +- .../ConfirmAccountProviderNode.kt | 6 +- .../ConfirmAccountProviderView.kt | 8 +- .../impl/screens/onboarding/OnBoardingNode.kt | 6 +- .../impl/screens/onboarding/OnBoardingView.kt | 8 +- .../login/impl/DefaultLoginEntryPointTest.kt | 4 +- .../changeserver/ChangeServerPresenterTest.kt | 2 +- .../impl/qrcode/QrCodeLoginFlowNodeTest.kt | 2 +- .../ChooseAccountProviderViewTest.kt | 6 +- .../ConfirmAccountProviderPresenterTest.kt | 88 +++++++++---------- .../onboarding/OnBoardingPresenterTest.kt | 8 +- .../screens/onboarding/OnboardingViewTest.kt | 18 ++-- .../impl/root/PreferencesRootPresenterTest.kt | 2 +- features/securebackup/impl/build.gradle.kts | 2 +- .../impl/reset/ResetIdentityFlowNode.kt | 10 +-- .../signedout/impl/SignedOutStateProvider.kt | 2 +- .../libraries/matrix/api/MatrixClient.kt | 2 +- .../api/auth/AuthenticationException.kt | 2 +- .../api/auth/MatrixAuthenticationService.kt | 18 ++-- .../api/auth/MatrixHomeServerDetails.kt | 4 +- .../auth/{OidcConfig.kt => OAuthConfig.kt} | 2 +- .../auth/{OidcDetails.kt => OAuthDetails.kt} | 2 +- .../auth/{OidcPrompt.kt => OAuthPrompt.kt} | 8 +- ...rovider.kt => OAuthRedirectUrlProvider.kt} | 2 +- .../api/auth/qrlogin/QrLoginException.kt | 2 +- .../api/encryption/EncryptionService.kt | 8 +- .../AccountManagementAction.kt | 2 +- .../api/auth/MatrixHomeServerDetailsTest.kt | 8 +- .../libraries/matrix/impl/RustMatrixClient.kt | 4 +- .../matrix/impl/RustMatrixClientFactory.kt | 2 +- .../impl/auth/AuthenticationException.kt | 10 +-- .../matrix/impl/auth/HomeserverDetails.kt | 2 +- ...vider.kt => OAuthConfigurationProvider.kt} | 20 ++--- .../libraries/matrix/impl/auth/OidcPrompt.kt | 12 +-- ...RustHomeServerLoginCompatibilityChecker.kt | 2 +- .../auth/RustMatrixAuthenticationService.kt | 32 +++---- .../matrix/impl/auth/qrlogin/QrErrorMapper.kt | 2 +- .../encryption/RustIdentityResetHandle.kt | 10 +-- .../libraries/matrix/impl/mapper/Session.kt | 4 +- .../AccountManagementAction.kt | 4 +- .../AuthenticationExceptionMappingTest.kt | 12 +-- .../impl/auth/HomeserverDetailsKtTest.kt | 4 +- ...t.kt => OAuthConfigurationProviderTest.kt} | 8 +- ...HomeserverLoginCompatibilityCheckerTest.kt | 4 +- .../RustMatrixAuthenticationServiceTest.kt | 6 +- .../impl/auth/qrlogin/QrErrorMapperTest.kt | 2 +- .../fakes/FakeFfiHomeserverLoginDetails.kt | 4 +- .../matrix/impl/mapper/SessionKtTest.kt | 4 +- .../AccountManagementActionKtTest.kt | 4 +- .../libraries/matrix/test/FakeMatrixClient.kt | 2 +- .../auth/FakeMatrixAuthenticationService.kt | 32 +++---- ...der.kt => FakeOAuthRedirectUrlProvider.kt} | 6 +- .../test/auth/MatrixHomeServerDetails.kt | 4 +- .../encryption/FakeIdentityResetHandle.kt | 12 +-- .../{oidc => oauth}/api/build.gradle.kts | 2 +- .../libraries/oauth/api/OAuthAction.kt} | 8 +- .../libraries/oauth/api/OAuthActionFlow.kt} | 8 +- .../oauth/api/OAuthIntentResolver.kt} | 6 +- .../{oidc => oauth}/impl/build.gradle.kts | 4 +- .../oauth/impl/DefaultOAuthActionFlow.kt} | 16 ++-- .../oauth/impl/DefaultOAuthIntentResolver.kt | 24 +++++ .../libraries/oauth/impl/OAuthUrlParser.kt} | 28 +++--- .../oauth/impl/DefaultOAuthActionFlowTest.kt} | 17 ++-- .../impl/DefaultOAuthIntentResolverTest.kt} | 35 ++++---- .../oauth/impl/DefaultOAuthUrlParserTest.kt} | 31 ++++--- .../{oidc => oauth}/test/build.gradle.kts | 4 +- .../oauth/test/FakeOAuthIntentResolver.kt} | 14 +-- .../test/customtab/FakeOAuthActionFlow.kt | 33 +++++++ .../oidc/impl/DefaultOidcIntentResolver.kt | 24 ----- .../oidc/test/customtab/FakeOidcActionFlow.kt | 33 ------- .../sessionstorage/api/SessionData.kt | 4 +- .../sessionstorage/impl/SessionDataMapper.kt | 4 +- .../sessionstorage/test/SessionData.kt | 2 +- .../kotlin/extension/DependencyHandleScope.kt | 2 +- 96 files changed, 479 insertions(+), 482 deletions(-) rename app/src/main/kotlin/io/element/android/x/oidc/{DefaultOidcRedirectUrlProvider.kt => DefaultOAuthRedirectUrlProvider.kt} (82%) rename app/src/test/kotlin/io/element/android/x/oidc/{DefaultOidcRedirectUrlProviderTest.kt => DefaultOAuthRedirectUrlProviderTest.kt} (89%) rename docs/{oidc.md => oauth.md} (81%) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/{OidcConfig.kt => OAuthConfig.kt} (97%) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/{OidcDetails.kt => OAuthDetails.kt} (94%) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/{OidcPrompt.kt => OAuthPrompt.kt} (81%) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/{OidcRedirectUrlProvider.kt => OAuthRedirectUrlProvider.kt} (89%) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/{oidc => oauth}/AccountManagementAction.kt (91%) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/{OidcConfigurationProvider.kt => OAuthConfigurationProvider.kt} (53%) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{oidc => oauth}/AccountManagementAction.kt (86%) rename libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/{OidcConfigurationProviderTest.kt => OAuthConfigurationProviderTest.kt} (76%) rename libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/{oidc => oauth}/AccountManagementActionKtTest.kt (90%) rename libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/{FakeOidcRedirectUrlProvider.kt => FakeOAuthRedirectUrlProvider.kt} (75%) rename libraries/{oidc => oauth}/api/build.gradle.kts (87%) rename libraries/{oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt => oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthAction.kt} (54%) rename libraries/{oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcActionFlow.kt => oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt} (63%) rename libraries/{oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcIntentResolver.kt => oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt} (68%) rename libraries/{oidc => oauth}/impl/build.gradle.kts (93%) rename libraries/{oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlow.kt => oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlow.kt} (58%) create mode 100644 libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolver.kt rename libraries/{oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt => oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/OAuthUrlParser.kt} (51%) rename libraries/{oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt => oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlowTest.kt} (58%) rename libraries/{oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt => oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolverTest.kt} (64%) rename libraries/{oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt => oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthUrlParserTest.kt} (56%) rename libraries/{oidc => oauth}/test/build.gradle.kts (80%) rename libraries/{oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/FakeOidcIntentResolver.kt => oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/FakeOAuthIntentResolver.kt} (50%) create mode 100644 libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/customtab/FakeOAuthActionFlow.kt delete mode 100644 libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolver.kt delete mode 100644 libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/customtab/FakeOidcActionFlow.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4ee1c8459..da90ec82e4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -103,13 +103,13 @@ android { logger.warnInBox("Building ${defaultConfig.applicationId} ($baseAppName) [$buildType]") buildTypes { - val oidcRedirectSchemeBase = BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element.android" + val oAuthRedirectSchemeBase = BuildTimeConfig.METADATA_HOST_REVERSED ?: "io.element.android" getByName("debug") { resValue("string", "app_name", "$baseAppName dbg") resValue( "string", "login_redirect_scheme", - "$oidcRedirectSchemeBase.debug", + "$oAuthRedirectSchemeBase.debug", ) applicationIdSuffix = ".debug" signingConfig = signingConfigs.getByName("debug") @@ -120,7 +120,7 @@ android { resValue( "string", "login_redirect_scheme", - oidcRedirectSchemeBase, + oAuthRedirectSchemeBase, ) signingConfig = signingConfigs.getByName("debug") @@ -157,7 +157,7 @@ android { resValue( "string", "login_redirect_scheme", - "$oidcRedirectSchemeBase.nightly", + "$oAuthRedirectSchemeBase.nightly", ) matchingFallbacks += listOf("release") signingConfig = signingConfigs.getByName("nightly") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6041fbb118..d63e18ec1a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -75,7 +75,7 @@ android:scheme="elementx" /> diff --git a/app/src/main/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProvider.kt b/app/src/main/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProvider.kt similarity index 82% rename from app/src/main/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProvider.kt rename to app/src/main/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProvider.kt index ad4f9a47b2..16db564aaf 100644 --- a/app/src/main/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProvider.kt +++ b/app/src/main/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProvider.kt @@ -10,14 +10,14 @@ package io.element.android.x.oidc import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider +import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider import io.element.android.services.toolbox.api.strings.StringProvider import io.element.android.x.R @ContributesBinding(AppScope::class) -class DefaultOidcRedirectUrlProvider( +class DefaultOAuthRedirectUrlProvider( private val stringProvider: StringProvider, -) : OidcRedirectUrlProvider { +) : OAuthRedirectUrlProvider { override fun provide() = buildString { append(stringProvider.getString(R.string.login_redirect_scheme)) append(":/") diff --git a/app/src/test/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProviderTest.kt b/app/src/test/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProviderTest.kt similarity index 89% rename from app/src/test/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProviderTest.kt rename to app/src/test/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProviderTest.kt index 18567355d2..c26e3dc692 100644 --- a/app/src/test/kotlin/io/element/android/x/oidc/DefaultOidcRedirectUrlProviderTest.kt +++ b/app/src/test/kotlin/io/element/android/x/oidc/DefaultOAuthRedirectUrlProviderTest.kt @@ -13,13 +13,13 @@ import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.x.R import org.junit.Test -class DefaultOidcRedirectUrlProviderTest { +class DefaultOAuthRedirectUrlProviderTest { @Test fun `test provide`() { val stringProvider = FakeStringProvider( defaultResult = "str" ) - val sut = DefaultOidcRedirectUrlProvider( + val sut = DefaultOAuthRedirectUrlProvider( stringProvider = stringProvider, ) val result = sut.provide() diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 24a0355b3f..6be468b0d1 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { implementation(projects.libraries.deeplink.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.oidc.api) + implementation(projects.libraries.oauth.api) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) implementation(projects.libraries.pushproviders.api) @@ -59,7 +59,7 @@ dependencies { testImplementation(projects.features.login.test) testImplementation(projects.features.share.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.oidc.test) + testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index 0e458d3b9c..acf7b66db9 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -63,8 +63,8 @@ import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.ui.common.nodes.emptyNode @@ -95,7 +95,7 @@ class RootFlowNode( private val signedOutEntryPoint: SignedOutEntryPoint, private val accountSelectEntryPoint: AccountSelectEntryPoint, private val intentResolver: IntentResolver, - private val oidcActionFlow: OidcActionFlow, + private val oAuthActionFlow: OAuthActionFlow, private val featureFlagService: FeatureFlagService, private val announcementService: AnnouncementService, private val analyticsService: AnalyticsService, @@ -392,7 +392,7 @@ class RootFlowNode( navigateTo(resolvedIntent.deeplinkData) } is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params) - is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction) + is ResolvedIntent.OAuth -> onOAuthAction(resolvedIntent.oAuthAction) is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData) is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.shareIntentData) } @@ -529,8 +529,8 @@ class RootFlowNode( } } - private fun onOidcAction(oidcAction: OidcAction) { - oidcActionFlow.post(oidcAction) + private fun onOAuthAction(oAuthAction: OAuthAction) { + oAuthActionFlow.post(oAuthAction) } private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt index 6844db3ed6..ee316f00aa 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt @@ -18,13 +18,13 @@ import io.element.android.libraries.deeplink.api.DeeplinkData import io.element.android.libraries.deeplink.api.DeeplinkParser import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcIntentResolver +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthIntentResolver import timber.log.Timber sealed interface ResolvedIntent { data class Navigation(val deeplinkData: DeeplinkData) : ResolvedIntent - data class Oidc(val oidcAction: OidcAction) : ResolvedIntent + data class OAuth(val oAuthAction: OAuthAction) : ResolvedIntent data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent data class Login(val params: LoginParams) : ResolvedIntent data class IncomingShare(val shareIntentData: ShareIntentData) : ResolvedIntent @@ -34,7 +34,7 @@ sealed interface ResolvedIntent { class IntentResolver( private val deeplinkParser: DeeplinkParser, private val loginIntentResolver: LoginIntentResolver, - private val oidcIntentResolver: OidcIntentResolver, + private val oAuthIntentResolver: OAuthIntentResolver, private val permalinkParser: PermalinkParser, private val shareIntentHandler: ShareIntentHandler, ) { @@ -45,9 +45,9 @@ class IntentResolver( val deepLinkData = deeplinkParser.getFromIntent(intent) if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData) - // Coming during login using Oidc? - val oidcAction = oidcIntentResolver.resolve(intent) - if (oidcAction != null) return ResolvedIntent.Oidc(oidcAction) + // Coming during login using OAuth? + val oAuthAction = oAuthIntentResolver.resolve(intent) + if (oAuthAction != null) return ResolvedIntent.OAuth(oAuthAction) val actionViewData = intent .takeIf { it.action == Intent.ACTION_VIEW } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt index 576e1aaea6..451ca279f8 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/intent/IntentResolverTest.kt @@ -26,8 +26,8 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.test.FakeOidcIntentResolver +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.test.FakeOAuthIntentResolver import io.element.android.tests.testutils.lambda.lambdaError import org.junit.Test import org.junit.runner.RunWith @@ -170,9 +170,9 @@ class IntentResolverTest { } @Test - fun `test resolve oidc`() { + fun `test resolve OAuth`() { val sut = createIntentResolver( - oidcIntentResolverResult = { OidcAction.GoBack() }, + oAuthIntentResolverResult = { OAuthAction.GoBack() }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -180,8 +180,8 @@ class IntentResolverTest { } val result = sut.resolve(intent) assertThat(result).isEqualTo( - ResolvedIntent.Oidc( - oidcAction = OidcAction.GoBack() + ResolvedIntent.OAuth( + oAuthAction = OAuthAction.GoBack() ) ) } @@ -194,7 +194,7 @@ class IntentResolverTest { val sut = createIntentResolver( loginIntentResolverResult = { null }, permalinkParserResult = { permalinkData }, - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -213,7 +213,7 @@ class IntentResolverTest { val sut = createIntentResolver( permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) }, loginIntentResolverResult = { null }, - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -230,7 +230,7 @@ class IntentResolverTest { ) val sut = createIntentResolver( permalinkParserResult = { permalinkData }, - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_BATTERY_LOW @@ -244,7 +244,7 @@ class IntentResolverTest { fun `test incoming share simple`() { val shareIntentData = ShareIntentData.PlainText("Hello") val sut = createIntentResolver( - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, onIncomingShareIntent = { shareIntentData }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { @@ -260,7 +260,7 @@ class IntentResolverTest { val fileUri = "content://com.example.app/file1.jpg".toUri() val shareIntentData = ShareIntentData.Uris(text = "Hello", uris = listOf(UriToShare(fileUri, "image/jpg"))) val sut = createIntentResolver( - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, onIncomingShareIntent = { shareIntentData }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { @@ -277,7 +277,7 @@ class IntentResolverTest { val sut = createIntentResolver( permalinkParserResult = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) }, loginIntentResolverResult = { null }, - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -292,7 +292,7 @@ class IntentResolverTest { val aLoginParams = LoginParams("accountProvider", null) val sut = createIntentResolver( loginIntentResolverResult = { aLoginParams }, - oidcIntentResolverResult = { null }, + oAuthIntentResolverResult = { null }, ) val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW @@ -306,7 +306,7 @@ class IntentResolverTest { deeplinkParserResult: DeeplinkData? = null, permalinkParserResult: (String) -> PermalinkData = { lambdaError() }, loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() }, - oidcIntentResolverResult: (Intent) -> OidcAction? = { lambdaError() }, + oAuthIntentResolverResult: (Intent) -> OAuthAction? = { lambdaError() }, onIncomingShareIntent: (Intent) -> ShareIntentData? = { null }, ): IntentResolver { return IntentResolver( @@ -314,8 +314,8 @@ class IntentResolverTest { loginIntentResolver = FakeLoginIntentResolver( parseResult = loginIntentResolverResult, ), - oidcIntentResolver = FakeOidcIntentResolver( - resolveResult = oidcIntentResolverResult, + oAuthIntentResolver = FakeOAuthIntentResolver( + resolveResult = oAuthIntentResolverResult, ), permalinkParser = FakePermalinkParser( result = permalinkParserResult diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index 902f446a6f..18c8cfd7b9 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId 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.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion import io.element.android.libraries.matrix.api.sync.SyncState diff --git a/docs/oidc.md b/docs/oauth.md similarity index 81% rename from docs/oidc.md rename to docs/oauth.md index 23709b608c..1080c64b0e 100644 --- a/docs/oidc.md +++ b/docs/oauth.md @@ -1,4 +1,4 @@ -This file contains some rough notes about Oidc implementation, with some examples of actual data. +This file contains some rough notes about OAuth implementation, with some examples of actual data. [ios implementation](https://github.com/element-hq/element-x-ios/compare/develop...doug/oidc-temp) @@ -25,7 +25,7 @@ tosUri = "https://element.io/user-terms-of-service", policyUri = "https://element.io/privacy" -Example of OidcData (from presentUrl callback): +Example of OAuthData (from presentUrl callback): url: https://auth-oidc.lab.element.dev/authorize?response_type=code&client_id=01GYCAGG3PA70CJ97ZVP0WFJY3&redirect_uri=io.element%3A%2Fcallback&scope=openid+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Aapi%3A*+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Adevice%3AYAgcPW4mcG&state=ex6mNJVFZ5jn9wL8&nonce=NZ93DOyIGQd9exPQ&code_challenge_method=S256&code_challenge=FFRcPALNSPCh-ZgpyTRFu_h8NZJVncfvihbfT9CyX8U&prompt=consent Formatted url: @@ -43,8 +43,8 @@ https://auth-oidc.lab.element.dev/authorize? state: ex6mNJVFZ5jn9wL8 -Oidc client example: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/examples/oidc_cli/src/main.rs -Oidc sdk doc: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/crates/matrix-sdk/src/oidc.rs +OAuth client example: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/examples/oidc_cli/src/main.rs +OAuth sdk doc: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/crates/matrix-sdk/src/oidc.rs Test server: diff --git a/features/linknewdevice/impl/build.gradle.kts b/features/linknewdevice/impl/build.gradle.kts index 9c1aa9e990..adbec91e6a 100644 --- a/features/linknewdevice/impl/build.gradle.kts +++ b/features/linknewdevice/impl/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.qrcode) - implementation(projects.libraries.oidc.api) + implementation(projects.libraries.oauth.api) implementation(projects.libraries.uiUtils) implementation(projects.libraries.wellknown.api) implementation(libs.androidx.browser) @@ -56,7 +56,7 @@ dependencies { testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.oidc.test) + testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.wellknown.test) diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 12af922cbe..e739beb20a 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -69,7 +69,7 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.qrcode) - implementation(projects.libraries.oidc.api) + implementation(projects.libraries.oauth.api) implementation(projects.libraries.uiUtils) implementation(projects.libraries.wellknown.api) implementation(libs.androidx.browser) @@ -83,7 +83,7 @@ dependencies { testImplementation(projects.features.preferences.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.oidc.test) + testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.wellknown.test) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index fb384d505a..978d28dfa3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -50,9 +50,9 @@ import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.matrix.api.auth.OidcDetails -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow +import io.element.android.libraries.matrix.api.auth.OAuthDetails +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -64,7 +64,7 @@ class LoginFlowNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, private val accountProviderDataSource: AccountProviderDataSource, - private val oidcActionFlow: OidcActionFlow, + private val oAuthActionFlow: OAuthActionFlow, @AppCoroutineScope private val appCoroutineScope: CoroutineScope, private val elementClassicConnection: ElementClassicConnection, @@ -100,7 +100,7 @@ class LoginFlowNode( // by pressing back or by closing the Custom Chrome Tab. lifecycleScope.launch { delay(5000) - oidcActionFlow.post(OidcAction.GoBack(toUnblock = true)) + oAuthActionFlow.post(OAuthAction.GoBack(toUnblock = true)) } } } @@ -161,8 +161,8 @@ class LoginFlowNode( backstack.push(NavTarget.LoginPassword()) } - override fun navigateToOidc(oidcDetails: OidcDetails) { - navigateToMas(oidcDetails) + override fun navigateToOAuth(oAuthDetails: OAuthDetails) { + navigateToMas(oAuthDetails) } override fun navigateToCreateAccount(url: String) { @@ -197,8 +197,8 @@ class LoginFlowNode( callback.navigateToBugReport() } - override fun navigateToOidc(oidcDetails: OidcDetails) { - navigateToMas(oidcDetails) + override fun navigateToOAuth(oAuthDetails: OAuthDetails) { + navigateToMas(oAuthDetails) } override fun navigateToCreateAccount(url: String) { @@ -243,8 +243,8 @@ class LoginFlowNode( } NavTarget.ChooseAccountProvider -> { val callback = object : ChooseAccountProviderNode.Callback { - override fun navigateToOidc(oidcDetails: OidcDetails) { - navigateToMas(oidcDetails) + override fun navigateToOAuth(oAuthDetails: OAuthDetails) { + navigateToMas(oAuthDetails) } override fun navigateToCreateAccount(url: String) { @@ -270,8 +270,8 @@ class LoginFlowNode( isAccountCreation = navTarget.isAccountCreation, ) val callback = object : ConfirmAccountProviderNode.Callback { - override fun navigateToOidc(oidcDetails: OidcDetails) { - navigateToMas(oidcDetails) + override fun navigateToOAuth(oAuthDetails: OAuthDetails) { + navigateToMas(oAuthDetails) } override fun navigateToCreateAccount(url: String) { @@ -333,10 +333,10 @@ class LoginFlowNode( } } - private fun navigateToMas(oidcDetails: OidcDetails) { + private fun navigateToMas(oAuthDetails: OAuthDetails) { activity?.let { externalAppStarted = true - it.openUrlInChromeCustomTab(null, darkTheme, oidcDetails.url) + it.openUrlInChromeCustomTab(null, darkTheme, oAuthDetails.url) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt index 2f4af14237..560e6123c1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt @@ -41,7 +41,7 @@ sealed class ChangeServerError : Exception() { // AccountAlreadyLoggedIn error should not happen at this point is AuthenticationException.AccountAlreadyLoggedIn -> Error(messageStr = error.message) is AuthenticationException.Generic -> Error(messageStr = error.message) - is AuthenticationException.Oidc -> Error(messageStr = error.message) + is AuthenticationException.OAuth -> Error(messageStr = error.message) } } is AccountProviderAccessException.NeedElementProException -> NeedElementPro( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt index 78be770bfc..3c871a8a1d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginHelper.kt @@ -23,9 +23,9 @@ import io.element.android.features.login.impl.web.WebClientUrlForAuthenticationR import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService -import io.element.android.libraries.matrix.api.auth.OidcPrompt -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow +import io.element.android.libraries.matrix.api.auth.OAuthPrompt +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow /** * This class is responsible for managing the login flow, including handling OIDC actions and @@ -35,7 +35,7 @@ import io.element.android.libraries.oidc.api.OidcActionFlow */ @Inject class LoginHelper( - private val oidcActionFlow: OidcActionFlow, + private val oAuthActionFlow: OAuthActionFlow, private val authenticationService: MatrixAuthenticationService, private val webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever, ) { @@ -44,9 +44,9 @@ class LoginHelper( @Composable fun collectLoginMode(): State> { LaunchedEffect(Unit) { - oidcActionFlow.collect { oidcAction -> - if (oidcAction != null) { - onOidcAction(oidcAction) + oAuthActionFlow.collect { oAuthAction -> + if (oAuthAction != null) { + onOAuthAction(oAuthAction) } } } @@ -73,11 +73,11 @@ class LoginHelper( throw it } }.map { matrixHomeServerDetails -> - if (matrixHomeServerDetails.supportsOidcLogin) { + if (matrixHomeServerDetails.supportsOAuthLogin) { // Retrieve the details right now - val oidcPrompt = if (isAccountCreation) OidcPrompt.Create else OidcPrompt.Login - LoginMode.Oidc( - authenticationService.getOidcUrl(prompt = oidcPrompt, loginHint = loginHint).getOrThrow() + val oAuthPrompt = if (isAccountCreation) OAuthPrompt.Create else OAuthPrompt.Login + LoginMode.OAuth( + authenticationService.getOAuthUrl(prompt = oAuthPrompt, loginHint = loginHint).getOrThrow() ) } else if (isAccountCreation) { val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) @@ -99,16 +99,16 @@ class LoginHelper( ) } - private suspend fun onOidcAction(oidcAction: OidcAction) { - if (oidcAction is OidcAction.GoBack && oidcAction.toUnblock && loginModeState.value !is AsyncData.Loading) { + private suspend fun onOAuthAction(oAuthAction: OAuthAction) { + if (oAuthAction is OAuthAction.GoBack && oAuthAction.toUnblock && loginModeState.value !is AsyncData.Loading) { // Ignore GoBack action if the current state is not Loading. This GoBack action is coming from LoginFlowNode. // This can happen if there is an error, for instance attempt to login again on the same account. return } loginModeState.value = AsyncData.Loading() - when (oidcAction) { - is OidcAction.GoBack -> { - authenticationService.cancelOidcLogin() + when (oAuthAction) { + is OAuthAction.GoBack -> { + authenticationService.cancelOAuthLogin() .onSuccess { loginModeState.value = AsyncData.Uninitialized } @@ -116,13 +116,13 @@ class LoginHelper( loginModeState.value = AsyncData.Failure(failure) } } - is OidcAction.Success -> { - authenticationService.loginWithOidc(oidcAction.url) + is OAuthAction.Success -> { + authenticationService.loginWithOAuth(oAuthAction.url) .onFailure { failure -> loginModeState.value = AsyncData.Failure(failure) } } } - oidcActionFlow.reset() + oAuthActionFlow.reset() } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginMode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginMode.kt index 08e604ef20..5ea52e0ebd 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginMode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginMode.kt @@ -8,10 +8,10 @@ package io.element.android.features.login.impl.login -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails sealed interface LoginMode { data object PasswordLogin : LoginMode - data class Oidc(val oidcDetails: OidcDetails) : LoginMode + data class OAuth(val oAuthDetails: OAuthDetails) : LoginMode data class AccountCreation(val url: String) : LoginMode } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt index f88e34bf4a..3549e17457 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt @@ -24,7 +24,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.matrix.api.auth.AuthenticationException -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -32,7 +32,7 @@ fun LoginModeView( loginMode: AsyncData, onClearError: () -> Unit, onLearnMoreClick: () -> Unit, - onOidcDetails: (OidcDetails) -> Unit, + onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onCreateAccountContinue: (url: String) -> Unit ) { @@ -118,7 +118,7 @@ fun LoginModeView( is AsyncData.Loading -> Unit // The Continue button shows the loading state is AsyncData.Success -> { when (val loginModeData = loginMode.data) { - is LoginMode.Oidc -> onOidcDetails(loginModeData.oidcDetails) + is LoginMode.OAuth -> onOAuthDetails(loginModeData.oAuthDetails) LoginMode.PasswordLogin -> onNeedLoginPassword() is LoginMode.AccountCreation -> onCreateAccountContinue(loginModeData.url) } @@ -137,7 +137,7 @@ internal fun LoginModeViewPreview(@PreviewParameter(LoginModeViewErrorProvider:: loginMode = AsyncData.Failure(error), onClearError = {}, onLearnMoreClick = {}, - onOidcDetails = {}, + onOAuthDetails = {}, onNeedLoginPassword = {}, onCreateAccountContinue = {} ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt index 613aa6aeb6..03264551a5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt @@ -135,8 +135,8 @@ class QrCodeLoginFlowNode( is QrLoginException.SlidingSyncNotAvailable -> { backstack.replace(NavTarget.Error(QrCodeErrorScreenType.SlidingSyncNotAvailable)) } - is QrLoginException.OidcMetadataInvalid -> { - Timber.e(error, "OIDC metadata is invalid") + is QrLoginException.OAuthMetadataInvalid -> { + Timber.e(error, "OAuth metadata is invalid") backstack.replace(NavTarget.Error(QrCodeErrorScreenType.UnknownError)) } QrLoginException.CheckCodeAlreadySent, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt index 5dc6ebbd6b..5f79f197d9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt @@ -20,7 +20,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.callback -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails @ContributesNode(AppScope::class) @AssistedInject @@ -31,7 +31,7 @@ class ChooseAccountProviderNode( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun navigateToLoginPassword() - fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) } @@ -45,7 +45,7 @@ class ChooseAccountProviderNode( state = state, modifier = modifier, onBackClick = ::navigateUp, - onOidcDetails = callback::navigateToOidc, + onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, onCreateAccountContinue = callback::navigateToCreateAccount, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt index cdb80304a7..f05606dbc3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderView.kt @@ -43,14 +43,14 @@ 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.Scaffold import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ChooseAccountProviderView( state: ChooseAccountProviderState, onBackClick: () -> Unit, - onOidcDetails: (OidcDetails) -> Unit, + onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, onCreateAccountContinue: (url: String) -> Unit, @@ -129,7 +129,7 @@ fun ChooseAccountProviderView( state.eventSink(ChooseAccountProviderEvents.ClearError) }, onLearnMoreClick = onLearnMoreClick, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onCreateAccountContinue = onCreateAccountContinue, ) @@ -144,7 +144,7 @@ internal fun ChooseAccountProviderViewPreview(@PreviewParameter(ChooseAccountPro state = state, onBackClick = { }, onLearnMoreClick = { }, - onOidcDetails = { }, + onOAuthDetails = { }, onNeedLoginPassword = { }, onCreateAccountContinue = { }, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt index f2ff998652..cfbd86f363 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/ClassicFlowNode.kt @@ -31,7 +31,7 @@ import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.rememberFaderOrSliderTransitionHandler import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.matrix.api.core.UserId import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -54,7 +54,7 @@ class ClassicFlowNode( interface Callback : Plugin { fun navigateToOnBoarding(allowBackNavigation: Boolean) fun navigateToLoginPassword() - fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) } @@ -111,8 +111,8 @@ class ClassicFlowNode( callback.navigateToLoginPassword() } - override fun navigateToOidc(oidcDetails: OidcDetails) { - callback.navigateToOidc(oidcDetails) + override fun navigateToOAuth(oAuthDetails: OAuthDetails) { + callback.navigateToOAuth(oAuthDetails) } override fun navigateToCreateAccount(url: String) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt index c42248a3f8..d5acca38ae 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicNode.kt @@ -21,7 +21,7 @@ import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.matrix.api.core.UserId @ContributesNode(AppScope::class) @@ -35,7 +35,7 @@ class LoginWithClassicNode( interface Callback : Plugin { fun navigateToOtherOptions() fun navigateToLoginPassword() - fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) fun navigateToMissingKeyBackup() } @@ -60,7 +60,7 @@ class LoginWithClassicNode( state = state, modifier = modifier, onOtherOptionsClick = callback::navigateToOtherOptions, - onOidcDetails = callback::navigateToOidc, + onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, onCreateAccountContinue = callback::navigateToCreateAccount, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt index 6b5c48f1ec..b1ca50fe61 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/classic/loginwithclassic/LoginWithClassicView.kt @@ -49,7 +49,7 @@ 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.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -59,7 +59,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun LoginWithClassicView( state: LoginWithClassicState, onOtherOptionsClick: () -> Unit, - onOidcDetails: (OidcDetails) -> Unit, + onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, onCreateAccountContinue: (url: String) -> Unit, @@ -200,7 +200,7 @@ fun LoginWithClassicView( state.eventSink(LoginWithClassicEvent.ClearError) }, onLearnMoreClick = onLearnMoreClick, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onCreateAccountContinue = onCreateAccountContinue, ) @@ -212,7 +212,7 @@ internal fun LoginWithClassicViewPreview(@PreviewParameter(LoginWithClassicState LoginWithClassicView( state = state, onOtherOptionsClick = {}, - onOidcDetails = {}, + onOAuthDetails = {}, onNeedLoginPassword = {}, onLearnMoreClick = {}, onCreateAccountContinue = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index e3643afbf2..928a493dc1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -22,7 +22,7 @@ import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails @ContributesNode(AppScope::class) @AssistedInject @@ -44,7 +44,7 @@ class ConfirmAccountProviderNode( interface Callback : Plugin { fun navigateToLoginPassword() - fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) fun navigateToChangeAccountProvider() } @@ -58,7 +58,7 @@ class ConfirmAccountProviderNode( ConfirmAccountProviderView( state = state, modifier = modifier, - onOidcDetails = callback::navigateToOidc, + onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onCreateAccountContinue = callback::navigateToCreateAccount, onChange = callback::navigateToChangeAccountProvider, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index a175ab556d..c2525f3756 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -38,7 +38,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ConfirmAccountProviderView( state: ConfirmAccountProviderState, - onOidcDetails: (OidcDetails) -> Unit, + onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, onCreateAccountContinue: (url: String) -> Unit, @@ -103,7 +103,7 @@ fun ConfirmAccountProviderView( eventSink(ConfirmAccountProviderEvents.ClearError) }, onLearnMoreClick = onLearnMoreClick, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onCreateAccountContinue = onCreateAccountContinue, ) @@ -117,7 +117,7 @@ internal fun ConfirmAccountProviderViewPreview( ) = ElementPreview { ConfirmAccountProviderView( state = state, - onOidcDetails = {}, + onOAuthDetails = {}, onNeedLoginPassword = {}, onCreateAccountContinue = {}, onLearnMoreClick = {}, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt index 5572c412a0..99f7e86fd3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt @@ -22,7 +22,7 @@ import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails @ContributesNode(AppScope::class) @AssistedInject @@ -40,7 +40,7 @@ class OnBoardingNode( fun navigateToQrCode() fun navigateToBugReport() fun navigateToLoginPassword() - fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToOAuth(oAuthDetails: OAuthDetails) fun navigateToCreateAccount(url: String) fun navigateToDeveloperSettings() fun onDone() @@ -71,7 +71,7 @@ class OnBoardingNode( onCreateAccount = callback::navigateToSignUpFlow, onSignInWithQrCode = callback::navigateToQrCode, onReportProblem = callback::navigateToBugReport, - onOidcDetails = callback::navigateToOidc, + onOAuthDetails = callback::navigateToOAuth, onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, onCreateAccountContinue = callback::navigateToCreateAccount, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt index 5ee7ab6ac4..53c36ac4f8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingView.kt @@ -50,7 +50,7 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @@ -68,7 +68,7 @@ fun OnBoardingView( onSignInWithQrCode: () -> Unit, onSignIn: (mustChooseAccountProvider: Boolean) -> Unit, onCreateAccount: () -> Unit, - onOidcDetails: (OidcDetails) -> Unit, + onOAuthDetails: (OAuthDetails) -> Unit, onNeedLoginPassword: () -> Unit, onLearnMoreClick: () -> Unit, onCreateAccountContinue: (url: String) -> Unit, @@ -82,7 +82,7 @@ fun OnBoardingView( state.eventSink(OnBoardingEvents.ClearError) }, onLearnMoreClick = onLearnMoreClick, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onCreateAccountContinue = onCreateAccountContinue, ) @@ -354,7 +354,7 @@ internal fun OnBoardingViewPreview( onSignIn = {}, onCreateAccount = {}, onReportProblem = {}, - onOidcDetails = {}, + onOAuthDetails = {}, onNeedLoginPassword = {}, onLearnMoreClick = {}, onCreateAccountContinue = {}, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt index 86a629270f..a05194d008 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt @@ -17,7 +17,7 @@ import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource import io.element.android.features.login.impl.classic.FakeElementClassicConnection import io.element.android.features.preferences.test.FakePreferencesEntryPoint -import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow +import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -39,7 +39,7 @@ class DefaultLoginEntryPointTest { buildContext = buildContext, plugins = plugins, accountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), - oidcActionFlow = FakeOidcActionFlow(), + oAuthActionFlow = FakeOAuthActionFlow(), appCoroutineScope = backgroundScope, elementClassicConnection = FakeElementClassicConnection(), preferencesEntryPoint = FakePreferencesEntryPoint(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 1fb5d37627..274b58ee49 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -50,7 +50,7 @@ class ChangeServerPresenterTest { fun `present - change server ok`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) createPresenter( diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNodeTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNodeTest.kt index 9d2628005c..112d8d7108 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNodeTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNodeTest.kt @@ -79,7 +79,7 @@ class QrCodeLoginFlowNodeTest { qrCodeLoginManager.currentLoginStep.value = QrCodeLoginStep.Failed(QrLoginException.ConnectionInsecure) assertThat(flowNode.currentNavTarget()).isEqualTo(QrCodeLoginFlowNode.NavTarget.Error(QrCodeErrorScreenType.InsecureChannelDetected)) - qrCodeLoginManager.currentLoginStep.value = QrCodeLoginStep.Failed(QrLoginException.OidcMetadataInvalid) + qrCodeLoginManager.currentLoginStep.value = QrCodeLoginStep.Failed(QrLoginException.OAuthMetadataInvalid) assertThat(flowNode.currentNavTarget()).isEqualTo(QrCodeLoginFlowNode.NavTarget.Error(QrCodeErrorScreenType.UnknownError)) qrCodeLoginManager.currentLoginStep.value = QrCodeLoginStep.Failed(QrLoginException.Unknown) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt index f7ff5d384d..c6610b212c 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.login.impl.accountprovider.anAccountProvider import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -84,7 +84,7 @@ class ChooseAccountProviderViewTest { private fun AndroidComposeTestRule.setChooseAccountProviderView( state: ChooseAccountProviderState, onBackClick: () -> Unit = EnsureNeverCalled(), - onOidcDetails: (OidcDetails) -> Unit = EnsureNeverCalledWithParam(), + onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(), onNeedLoginPassword: () -> Unit = EnsureNeverCalled(), onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onCreateAccountContinue: (url: String) -> Unit = EnsureNeverCalledWithParam(), @@ -93,7 +93,7 @@ class ChooseAccountProviderViewTest { ChooseAccountProviderView( state = state, onBackClick = onBackClick, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onLearnMoreClick = onLearnMoreClick, onCreateAccountContinue = onCreateAccountContinue, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 6372841250..a9045ab152 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -22,9 +22,9 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.auth.aMatrixHomeServerDetails -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow -import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow +import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest @@ -74,7 +74,7 @@ class ConfirmAccountProviderPresenterTest { fun `present - continue oidc`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -89,21 +89,21 @@ class ConfirmAccountProviderPresenterTest { val successState = awaitItem() assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) + assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) } } @Test - fun `present - oidc - cancel with failure`() = runTest { + fun `present - OAuth - cancel with failure`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) - val defaultOidcActionFlow = FakeOidcActionFlow() + val defaultOAuthActionFlow = FakeOAuthActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, - defaultOidcActionFlow = defaultOidcActionFlow, + defaultOAuthActionFlow = defaultOAuthActionFlow, ) presenter.test { val initialState = awaitItem() @@ -114,25 +114,25 @@ class ConfirmAccountProviderPresenterTest { val successState = awaitItem() assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) - authenticationService.givenOidcCancelError(AN_EXCEPTION) - defaultOidcActionFlow.post(OidcAction.GoBack()) + assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + authenticationService.givenOAuthCancelError(AN_EXCEPTION) + defaultOAuthActionFlow.post(OAuthAction.GoBack()) val cancelFailureState = awaitItem() assertThat(cancelFailureState.loginMode).isInstanceOf(AsyncData.Failure::class.java) } } @Test - fun `present - oidc - cancel with success`() = runTest { + fun `present - OAuth - cancel with success`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) - val defaultOidcActionFlow = FakeOidcActionFlow() + val defaultOAuthActionFlow = FakeOAuthActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, - defaultOidcActionFlow = defaultOidcActionFlow, + defaultOAuthActionFlow = defaultOAuthActionFlow, ) presenter.test { val initialState = awaitItem() @@ -143,24 +143,24 @@ class ConfirmAccountProviderPresenterTest { val successState = awaitItem() assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) - defaultOidcActionFlow.post(OidcAction.GoBack()) + assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + defaultOAuthActionFlow.post(OAuthAction.GoBack()) val cancelFinalState = awaitItem() assertThat(cancelFinalState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) } } @Test - fun `present - oidc - cancel to unblock`() = runTest { + fun `present - OAuth - cancel to unblock`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) - val defaultOidcActionFlow = FakeOidcActionFlow() + val defaultOAuthActionFlow = FakeOAuthActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, - defaultOidcActionFlow = defaultOidcActionFlow, + defaultOAuthActionFlow = defaultOAuthActionFlow, ) presenter.test { val initialState = awaitItem() @@ -168,23 +168,23 @@ class ConfirmAccountProviderPresenterTest { val loadingState = awaitItem() assertThat(loadingState.submitEnabled).isTrue() assertThat(loadingState.loginMode).isInstanceOf(AsyncData.Loading::class.java) - defaultOidcActionFlow.post(OidcAction.GoBack(toUnblock = true)) + defaultOAuthActionFlow.post(OAuthAction.GoBack(toUnblock = true)) val cancelFinalState = awaitItem() assertThat(cancelFinalState.loginMode).isInstanceOf(AsyncData.Uninitialized::class.java) } } @Test - fun `present - oidc - success with failure`() = runTest { + fun `present - OAuth - success with failure`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) - val defaultOidcActionFlow = FakeOidcActionFlow() + val defaultOAuthActionFlow = FakeOAuthActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, - defaultOidcActionFlow = defaultOidcActionFlow, + defaultOAuthActionFlow = defaultOAuthActionFlow, ) presenter.test { val initialState = awaitItem() @@ -195,9 +195,9 @@ class ConfirmAccountProviderPresenterTest { val successState = awaitItem() assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) + assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) authenticationService.givenLoginError(AN_EXCEPTION) - defaultOidcActionFlow.post(OidcAction.Success("aUrl")) + defaultOAuthActionFlow.post(OAuthAction.Success("aUrl")) val cancelLoadingState = awaitItem() assertThat(cancelLoadingState.loginMode).isInstanceOf(AsyncData.Loading::class.java) val cancelFailureState = awaitItem() @@ -206,16 +206,16 @@ class ConfirmAccountProviderPresenterTest { } @Test - fun `present - oidc - success with success`() = runTest { + fun `present - OAuth - success with success`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) - val defaultOidcActionFlow = FakeOidcActionFlow() + val defaultOidcActionFlow = FakeOAuthActionFlow() val presenter = createConfirmAccountProviderPresenter( matrixAuthenticationService = authenticationService, - defaultOidcActionFlow = defaultOidcActionFlow, + defaultOAuthActionFlow = defaultOidcActionFlow, ) presenter.test { val initialState = awaitItem() @@ -226,8 +226,8 @@ class ConfirmAccountProviderPresenterTest { val successState = awaitItem() assertThat(successState.submitEnabled).isFalse() assertThat(successState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) - defaultOidcActionFlow.post(OidcAction.Success("aUrl")) + assertThat(successState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) + defaultOidcActionFlow.post(OAuthAction.Success("aUrl")) val successSuccessState = awaitItem() assertThat(successSuccessState.loginMode).isInstanceOf(AsyncData.Loading::class.java) } @@ -311,10 +311,10 @@ class ConfirmAccountProviderPresenterTest { } @Test - fun `present - confirm account creation with oidc is successful`() = runTest { + fun `present - confirm account creation with OAuth is successful`() = runTest { val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -327,16 +327,16 @@ class ConfirmAccountProviderPresenterTest { skipItems(1) // Loading val submittedState = awaitItem() assertThat(submittedState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(submittedState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) + assertThat(submittedState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) } } @Test - fun `present - confirm account creation with oidc and url continues with oidc`() = runTest { + fun `present - confirm account creation with OAuth and url continues with OAuth`() = runTest { val aUrl = "aUrl" val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { - Result.success(aMatrixHomeServerDetails(supportsOidcLogin = true)) + Result.success(aMatrixHomeServerDetails(supportsOAuthLogin = true)) }, ) val presenter = createConfirmAccountProviderPresenter( @@ -350,12 +350,12 @@ class ConfirmAccountProviderPresenterTest { skipItems(1) // Loading val submittedState = awaitItem() assertThat(submittedState.loginMode).isInstanceOf(AsyncData.Success::class.java) - assertThat(submittedState.loginMode.dataOrNull()).isInstanceOf(LoginMode.Oidc::class.java) + assertThat(submittedState.loginMode.dataOrNull()).isInstanceOf(LoginMode.OAuth::class.java) } } @Test - fun `present - confirm account creation without oidc and with url continuing with url`() = runTest { + fun `present - confirm account creation without OAuth and with url continuing with url`() = runTest { val aUrl = "aUrl" val authenticationService = FakeMatrixAuthenticationService( setHomeserverResult = { @@ -380,14 +380,14 @@ class ConfirmAccountProviderPresenterTest { params: ConfirmAccountProviderPresenter.Params = ConfirmAccountProviderPresenter.Params(isAccountCreation = false), accountProviderDataSource: AccountProviderDataSource = AccountProviderDataSource(FakeEnterpriseService()), matrixAuthenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), - defaultOidcActionFlow: OidcActionFlow = FakeOidcActionFlow(), + defaultOAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), ) = ConfirmAccountProviderPresenter( params = params, accountProviderDataSource = accountProviderDataSource, loginHelper = createLoginHelper( authenticationService = matrixAuthenticationService, - oidcActionFlow = defaultOidcActionFlow, + oAuthActionFlow = defaultOAuthActionFlow, webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever, ), ) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt index 1fdfb7e070..8249694278 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenterTest.kt @@ -31,8 +31,8 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_LOGIN_HINT import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta -import io.element.android.libraries.oidc.api.OidcActionFlow -import io.element.android.libraries.oidc.test.customtab.FakeOidcActionFlow +import io.element.android.libraries.oauth.api.OAuthActionFlow +import io.element.android.libraries.oauth.test.customtab.FakeOAuthActionFlow import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData @@ -312,11 +312,11 @@ private fun createPresenter( ) fun createLoginHelper( - oidcActionFlow: OidcActionFlow = FakeOidcActionFlow(), + oAuthActionFlow: OAuthActionFlow = FakeOAuthActionFlow(), authenticationService: MatrixAuthenticationService = FakeMatrixAuthenticationService(), webClientUrlForAuthenticationRetriever: WebClientUrlForAuthenticationRetriever = FakeWebClientUrlForAuthenticationRetriever(), ): LoginHelper = LoginHelper( - oidcActionFlow = oidcActionFlow, + oAuthActionFlow = oAuthActionFlow, authenticationService = authenticationService, webClientUrlForAuthenticationRetriever = webClientUrlForAuthenticationRetriever, ) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt index ad09445075..a8f0ccbb5a 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt @@ -19,7 +19,7 @@ import com.google.testing.junit.testparameterinjector.TestParameter import io.element.android.features.login.impl.R import io.element.android.features.login.impl.login.LoginMode import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.api.auth.OidcDetails +import io.element.android.libraries.matrix.api.auth.OAuthDetails import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -224,14 +224,14 @@ class OnboardingViewTest { @Test fun `when success Oidc - the expected callback is invoked and the event is received`() { val eventSink = EventsRecorder() - val oidcDetails = OidcDetails("aUrl") - ensureCalledOnceWithParam(oidcDetails) { callback -> + val oAuthDetails = OAuthDetails("aUrl") + ensureCalledOnceWithParam(oAuthDetails) { callback -> rule.setOnboardingView( state = anOnBoardingState( - loginMode = AsyncData.Success(LoginMode.Oidc(oidcDetails)), + loginMode = AsyncData.Success(LoginMode.OAuth(oAuthDetails)), eventSink = eventSink, ), - onOidcDetails = callback, + onOAuthDetails = callback, ) } eventSink.assertSingle(OnBoardingEvents.ClearError) @@ -240,8 +240,8 @@ class OnboardingViewTest { @Test fun `when success AccountCreation - the expected callback is invoked and the event is received`() { val eventSink = EventsRecorder() - val oidcDetails = OidcDetails("aUrl") - ensureCalledOnceWithParam(oidcDetails.url) { callback -> + val oAuthDetails = OAuthDetails("aUrl") + ensureCalledOnceWithParam(oAuthDetails.url) { callback -> rule.setOnboardingView( state = anOnBoardingState( loginMode = AsyncData.Success(LoginMode.AccountCreation("aUrl")), @@ -261,7 +261,7 @@ class OnboardingViewTest { onSignIn: (Boolean) -> Unit = EnsureNeverCalledWithParam(), onCreateAccount: () -> Unit = EnsureNeverCalled(), onReportProblem: () -> Unit = EnsureNeverCalled(), - onOidcDetails: (OidcDetails) -> Unit = EnsureNeverCalledWithParam(), + onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(), onNeedLoginPassword: () -> Unit = EnsureNeverCalled(), onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onCreateAccountContinue: (url: String) -> Unit = EnsureNeverCalledWithParam(), @@ -275,7 +275,7 @@ class OnboardingViewTest { onSignIn = onSignIn, onCreateAccount = onCreateAccount, onReportProblem = onReportProblem, - onOidcDetails = onOidcDetails, + onOAuthDetails = onOAuthDetails, onNeedLoginPassword = onNeedLoginPassword, onLearnMoreClick = onLearnMoreClick, onCreateAccountContinue = onCreateAccountContinue, diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index e5d48ec175..d324f5eb3a 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -23,7 +23,7 @@ import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.indicator.test.FakeIndicatorService -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_SESSION_ID diff --git a/features/securebackup/impl/build.gradle.kts b/features/securebackup/impl/build.gradle.kts index b6117271f7..82f30fa5ce 100644 --- a/features/securebackup/impl/build.gradle.kts +++ b/features/securebackup/impl/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) - implementation(projects.libraries.oidc.api) + implementation(projects.libraries.oauth.api) implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) api(libs.statemachine) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt index c4a007f1d5..4e2284890f 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.architecture.createNode import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle +import io.element.android.libraries.matrix.api.encryption.IdentityOAuthResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -123,12 +123,12 @@ class ResetIdentityFlowNode( null -> { Timber.d("No reset handle return, the reset is done.") } - is IdentityOidcResetHandle -> { + is IdentityOAuthResetHandle -> { Timber.d("Launching reset confirmation in MAS") activity.openUrlInChromeCustomTab(null, darkTheme, handle.url) - Timber.d("Starting resetOidc") - resetJob = launch { handle.resetOidc() } - resetJob?.invokeOnCompletion { Timber.d("resetOidc ended") } + Timber.d("Starting resetOAuth") + resetJob = launch { handle.resetOAuth() } + resetJob?.invokeOnCompletion { Timber.d("resetOAuth ended") } } is IdentityPasswordResetHandle -> backstack.push(NavTarget.ResetPassword) } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt index 396339adbb..95a57db93e 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/SignedOutStateProvider.kt @@ -36,7 +36,7 @@ private fun aSessionData( accessToken = "anAccessToken", refreshToken = "aRefreshToken", homeserverUrl = "aHomeserverUrl", - oidcData = null, + oAuthData = null, loginTimestamp = null, isTokenValid = isTokenValid, loginType = LoginType.UNKNOWN, 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 35fd7e8551..59fd7a4940 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 @@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaPreviewService import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.JoinedRoom diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index c50ec09609..d5fd6a734a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -16,6 +16,6 @@ sealed class AuthenticationException(message: String?) : Exception(message) { class InvalidServerName(message: String?) : AuthenticationException(message) class SlidingSyncVersion(message: String?) : AuthenticationException(message) class ServerUnreachable(message: String?) : AuthenticationException(message) - class Oidc(message: String?) : AuthenticationException(message) + class OAuth(message: String?) : AuthenticationException(message) class Generic(message: String?) : AuthenticationException(message) } 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 7c82668242..04d1d13593 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 @@ -37,21 +37,21 @@ interface MatrixAuthenticationService { suspend fun importCreatedSession(externalSession: ExternalSession): Result /* - * OIDC part. + * OAuth part. */ /** - * Get the Oidc url to display to the user. + * Get the OAuth url to display to the user. */ - suspend fun getOidcUrl( - prompt: OidcPrompt, + suspend fun getOAuthUrl( + prompt: OAuthPrompt, loginHint: String?, - ): Result + ): Result /** - * Cancel Oidc login sequence. + * Cancel OAuth login sequence. */ - suspend fun cancelOidcLogin(): Result + suspend fun cancelOAuthLogin(): Result /** * Set the existing data about Element Classic session, if any. @@ -68,9 +68,9 @@ interface MatrixAuthenticationService { ): Boolean /** - * Attempt to login using the [callbackUrl] provided by the Oidc page. + * Attempt to log in using the [callbackUrl] provided by the OAuth page. */ - suspend fun loginWithOidc(callbackUrl: String): Result + suspend fun loginWithOAuth(callbackUrl: String): Result suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt index aa5ed9a41d..8dcb5c4a48 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.matrix.api.auth data class MatrixHomeServerDetails( val url: String, val supportsPasswordLogin: Boolean, - val supportsOidcLogin: Boolean, + val supportsOAuthLogin: Boolean, ) { - val isSupported = supportsPasswordLogin || supportsOidcLogin + val isSupported = supportsPasswordLogin || supportsOAuthLogin } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthConfig.kt similarity index 97% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthConfig.kt index ee8b7ec50e..d3a42f42b9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthConfig.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.api.auth import io.element.android.libraries.matrix.api.BuildConfig -object OidcConfig { +object OAuthConfig { const val CLIENT_URI = BuildConfig.CLIENT_URI // Note: host must match with the host of CLIENT_URI diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthDetails.kt similarity index 94% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthDetails.kt index c4fb87e3c2..d504f891ee 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthDetails.kt @@ -12,6 +12,6 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class OidcDetails( +data class OAuthDetails( val url: String, ) : Parcelable diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthPrompt.kt similarity index 81% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthPrompt.kt index 8ddad9f52e..45b4e18533 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthPrompt.kt @@ -8,12 +8,12 @@ package io.element.android.libraries.matrix.api.auth -sealed interface OidcPrompt { +sealed interface OAuthPrompt { /** * The Authorization Server should prompt the End-User for * reauthentication. */ - data object Login : OidcPrompt + data object Login : OAuthPrompt /** * The Authorization Server should prompt the End-User to create a user @@ -21,10 +21,10 @@ sealed interface OidcPrompt { * * Defined in [Initiating User Registration via OpenID Connect](https://openid.net/specs/openid-connect-prompt-create-1_0.html). */ - data object Create : OidcPrompt + data object Create : OAuthPrompt /** * An unknown value. */ - data class Unknown(val value: String) : OidcPrompt + data class Unknown(val value: String) : OAuthPrompt } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcRedirectUrlProvider.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt similarity index 89% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcRedirectUrlProvider.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt index ad4d862474..669d47501d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcRedirectUrlProvider.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OAuthRedirectUrlProvider.kt @@ -8,6 +8,6 @@ package io.element.android.libraries.matrix.api.auth -interface OidcRedirectUrlProvider { +interface OAuthRedirectUrlProvider { fun provide(): String } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/QrLoginException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/QrLoginException.kt index a3b567fa46..3f22244405 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/QrLoginException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/qrlogin/QrLoginException.kt @@ -15,7 +15,7 @@ sealed class QrLoginException : Exception() { data object Expired : QrLoginException() data object NotFound : QrLoginException() data object LinkingNotSupported : QrLoginException() - data object OidcMetadataInvalid : QrLoginException() + data object OAuthMetadataInvalid : QrLoginException() data object SlidingSyncNotAvailable : QrLoginException() data object OtherDeviceNotSignedIn : QrLoginException() data object CheckCodeAlreadySent : QrLoginException() diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index aefad517dc..333cfb709b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -112,19 +112,19 @@ interface IdentityPasswordResetHandle : IdentityResetHandle { } /** - * A handle to reset the user's identity with an OIDC login type. + * A handle to reset the user's identity with an OAuth login type. */ -interface IdentityOidcResetHandle : IdentityResetHandle { +interface IdentityOAuthResetHandle : IdentityResetHandle { /** * The URL to open in a webview/custom tab to reset the identity. */ val url: String /** - * Reset the identity using the OIDC flow. + * Reset the identity using the OAuth flow. * * This method will block the coroutine it's running on and keep polling indefinitely until either the coroutine is cancelled, the [cancel] method is * called, or the identity is reset. */ - suspend fun resetOidc(): Result + suspend fun resetOAuth(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oidc/AccountManagementAction.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oauth/AccountManagementAction.kt similarity index 91% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oidc/AccountManagementAction.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oauth/AccountManagementAction.kt index e1c7764e58..6dd0f6e53c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oidc/AccountManagementAction.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/oauth/AccountManagementAction.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.api.oidc +package io.element.android.libraries.matrix.api.oauth import io.element.android.libraries.matrix.api.core.DeviceId diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt index d4b360ef53..9babb7a738 100644 --- a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt +++ b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetailsTest.kt @@ -16,7 +16,7 @@ class MatrixHomeServerDetailsTest { @Test fun `if homeserver supports oidc, then it is supported`() { val sut = aMatrixHomeServerDetails( - supportsOidcLogin = true, + supportsOAuthLogin = true, supportsPasswordLogin = false, ) assertThat(sut.isSupported).isTrue() @@ -25,7 +25,7 @@ class MatrixHomeServerDetailsTest { @Test fun `if homeserver supports password, then it is supported`() { val sut = aMatrixHomeServerDetails( - supportsOidcLogin = false, + supportsOAuthLogin = false, supportsPasswordLogin = true, ) assertThat(sut.isSupported).isTrue() @@ -34,7 +34,7 @@ class MatrixHomeServerDetailsTest { @Test fun `if homeserver supports both, then it is supported`() { val sut = aMatrixHomeServerDetails( - supportsOidcLogin = true, + supportsOAuthLogin = true, supportsPasswordLogin = true, ) assertThat(sut.isSupported).isTrue() @@ -43,7 +43,7 @@ class MatrixHomeServerDetailsTest { @Test fun `if homeserver supports none, then it is not supported`() { val sut = aMatrixHomeServerDetails( - supportsOidcLogin = false, + supportsOAuthLogin = false, supportsPasswordLogin = false, ) assertThat(sut.isSupported).isFalse() 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 bb6806b5d4..5a1dc9da4b 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 @@ -31,7 +31,7 @@ import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler import io.element.android.libraries.matrix.api.media.MatrixMediaLoader -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.JoinedRoom @@ -59,7 +59,7 @@ import io.element.android.libraries.matrix.impl.media.RustMediaLoader import io.element.android.libraries.matrix.impl.media.RustMediaPreviewService import io.element.android.libraries.matrix.impl.notification.RustNotificationService import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService -import io.element.android.libraries.matrix.impl.oidc.toRustAction +import io.element.android.libraries.matrix.impl.oauth.toRustAction import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.GetRoomResult import io.element.android.libraries.matrix.impl.room.NotJoinedRustRoom 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 e5bae3bb9d..0bf26091c2 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 @@ -214,5 +214,5 @@ fun SessionData.toSession() = Session( deviceId = deviceId, homeserverUrl = homeserverUrl, slidingSyncVersion = SlidingSyncVersion.NATIVE, - oauthData = oidcData, + oauthData = oAuthData, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 2a151057b3..20dbf76a31 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -30,11 +30,11 @@ fun Throwable.mapAuthenticationException(): AuthenticationException { is ClientBuildException.EventCache -> AuthenticationException.Generic(message) } is OAuthException -> when (this) { - is OAuthException.Generic -> AuthenticationException.Oidc(message) - is OAuthException.CallbackUrlInvalid -> AuthenticationException.Oidc(message) - is OAuthException.Cancelled -> AuthenticationException.Oidc(message) - is OAuthException.MetadataInvalid -> AuthenticationException.Oidc(message) - is OAuthException.NotSupported -> AuthenticationException.Oidc(message) + is OAuthException.Generic -> AuthenticationException.OAuth(message) + is OAuthException.CallbackUrlInvalid -> AuthenticationException.OAuth(message) + is OAuthException.Cancelled -> AuthenticationException.OAuth(message) + is OAuthException.MetadataInvalid -> AuthenticationException.OAuth(message) + is OAuthException.NotSupported -> AuthenticationException.OAuth(message) } else -> AuthenticationException.Generic(message) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt index acf3a5a55b..810b8d8e0d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt @@ -15,6 +15,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use { MatrixHomeServerDetails( url = url(), supportsPasswordLogin = supportsPasswordLogin(), - supportsOidcLogin = supportsOauthLogin(), + supportsOAuthLogin = supportsOauthLogin(), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProvider.kt similarity index 53% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProvider.kt index 033b613dd8..d80dea2e7f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProvider.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProvider.kt @@ -10,22 +10,22 @@ package io.element.android.libraries.matrix.impl.auth import dev.zacsweers.metro.Inject import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.matrix.api.auth.OidcConfig -import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider +import io.element.android.libraries.matrix.api.auth.OAuthConfig +import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider import org.matrix.rustcomponents.sdk.OAuthConfiguration @Inject -class OidcConfigurationProvider( +class OAuthConfigurationProvider( private val buildMeta: BuildMeta, - private val oidcRedirectUrlProvider: OidcRedirectUrlProvider, + private val oAuthRedirectUrlProvider: OAuthRedirectUrlProvider, ) { fun get(): OAuthConfiguration = OAuthConfiguration( clientName = buildMeta.applicationName, - redirectUri = oidcRedirectUrlProvider.provide(), - clientUri = OidcConfig.CLIENT_URI, - logoUri = OidcConfig.LOGO_URI, - tosUri = OidcConfig.TOS_URI, - policyUri = OidcConfig.POLICY_URI, - staticRegistrations = OidcConfig.STATIC_REGISTRATIONS, + redirectUri = oAuthRedirectUrlProvider.provide(), + clientUri = OAuthConfig.CLIENT_URI, + logoUri = OAuthConfig.LOGO_URI, + tosUri = OAuthConfig.TOS_URI, + policyUri = OAuthConfig.POLICY_URI, + staticRegistrations = OAuthConfig.STATIC_REGISTRATIONS, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt index a23a6e5041..e97ddbee84 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt @@ -8,13 +8,13 @@ package io.element.android.libraries.matrix.impl.auth -import io.element.android.libraries.matrix.api.auth.OidcPrompt -import org.matrix.rustcomponents.sdk.OAuthPrompt as RustOidcPrompt +import io.element.android.libraries.matrix.api.auth.OAuthPrompt +import org.matrix.rustcomponents.sdk.OAuthPrompt as RustOAuthPrompt -internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt { +internal fun OAuthPrompt.toRustPrompt(): RustOAuthPrompt { return when (this) { - OidcPrompt.Login -> RustOidcPrompt.Unknown("consent") - OidcPrompt.Create -> RustOidcPrompt.Create - is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value) + OAuthPrompt.Login -> RustOAuthPrompt.Unknown("consent") + OAuthPrompt.Create -> RustOAuthPrompt.Create + is OAuthPrompt.Unknown -> RustOAuthPrompt.Unknown(value) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt index 4f59906023..3f8893138f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt @@ -31,7 +31,7 @@ class RustHomeServerLoginCompatibilityChecker( it.homeserverLoginDetails() } .use { - Timber.d("Homeserver $url | OIDC: ${it.supportsOauthLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}") + Timber.d("Homeserver $url | OAuth: ${it.supportsOauthLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}") it.supportsOauthLogin() || it.supportsPasswordLogin() } } 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 d186f06209..79793e4e77 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 @@ -19,8 +19,8 @@ import io.element.android.libraries.matrix.api.auth.AuthenticationException import io.element.android.libraries.matrix.api.auth.ElementClassicSession import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails -import io.element.android.libraries.matrix.api.auth.OidcDetails -import io.element.android.libraries.matrix.api.auth.OidcPrompt +import io.element.android.libraries.matrix.api.auth.OAuthDetails +import io.element.android.libraries.matrix.api.auth.OAuthPrompt import io.element.android.libraries.matrix.api.auth.SessionRestorationException import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData @@ -65,7 +65,7 @@ class RustMatrixAuthenticationService( private val sessionStore: SessionStore, private val rustMatrixClientFactory: RustMatrixClientFactory, private val passphraseGenerator: PassphraseGenerator, - private val oidcConfigurationProvider: OidcConfigurationProvider, + private val oAuthConfigurationProvider: OAuthConfigurationProvider, ) : MatrixAuthenticationService { // Any existing Element Classic session that we want to try to import secrets from during login. private var elementClassicSession: ElementClassicSession? = null @@ -253,15 +253,15 @@ class RustMatrixAuthenticationService( private var pendingOAuthAuthorizationData: OAuthAuthorizationData? = null - override suspend fun getOidcUrl( - prompt: OidcPrompt, + override suspend fun getOAuthUrl( + prompt: OAuthPrompt, loginHint: String?, - ): Result { + ): Result { return withContext(coroutineDispatchers.io) { runCatchingExceptions { val client = currentClient ?: error("You need to call `setHomeserver()` first") val oAuthAuthorizationData = client.urlForOauth( - oauthConfiguration = oidcConfigurationProvider.get(), + oauthConfiguration = oAuthConfigurationProvider.get(), prompt = prompt.toRustPrompt(), loginHint = loginHint, // If we want to restore a previous session for which we have encryption keys, we can pass the deviceId here. At the moment, we don't @@ -270,15 +270,15 @@ class RustMatrixAuthenticationService( ) val url = oAuthAuthorizationData.loginUrl() pendingOAuthAuthorizationData = oAuthAuthorizationData - OidcDetails(url) + OAuthDetails(url) }.mapFailure { failure -> - Timber.e(failure, "Failed to get OIDC URL") + Timber.e(failure, "Failed to get OAuth URL") failure.mapAuthenticationException() } } } - override suspend fun cancelOidcLogin(): Result { + override suspend fun cancelOAuthLogin(): Result { return withContext(coroutineDispatchers.io) { runCatchingExceptions { pendingOAuthAuthorizationData?.use { @@ -286,7 +286,7 @@ class RustMatrixAuthenticationService( } pendingOAuthAuthorizationData = null }.mapFailure { failure -> - Timber.e(failure, "Failed to cancel OIDC login") + Timber.e(failure, "Failed to cancel OAuth login") failure.mapAuthenticationException() } } @@ -297,9 +297,9 @@ class RustMatrixAuthenticationService( } /** - * callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters). + * callbackUrl should be the `url` from `OAuthAction` (with all the parameters). */ - override suspend fun loginWithOidc(callbackUrl: String): Result { + override suspend fun loginWithOAuth(callbackUrl: String): Result { return withContext(coroutineDispatchers.io) { runCatchingExceptions { val client = currentClient ?: error("You need to call `setHomeserver()` first") @@ -330,7 +330,7 @@ class RustMatrixAuthenticationService( SessionId(sessionData.userId) }.mapFailure { failure -> - Timber.e(failure, "Failed to login with OIDC") + Timber.e(failure, "Failed to login with OAuth") failure.mapAuthenticationException() } } @@ -355,7 +355,7 @@ class RustMatrixAuthenticationService( withContext(coroutineDispatchers.io) { val sdkQrCodeLoginData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData val emptySessionPaths = rotateSessionPath() - val oidcConfiguration = oidcConfigurationProvider.get() + val oAuthConfiguration = oAuthConfigurationProvider.get() val progressListener = object : QrLoginProgressListener { override fun onUpdate(state: QrLoginProgress) { Timber.d("QR Code login progress: $state") @@ -368,7 +368,7 @@ class RustMatrixAuthenticationService( qrCodeData = sdkQrCodeLoginData, ) client.newLoginWithQrCodeHandler( - oauthConfiguration = oidcConfiguration, + oauthConfiguration = oAuthConfiguration, ).use { it.scan( qrCodeData = qrCodeData.rustQrCodeData, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt index 1c4e300e91..23b9c6be5e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapper.kt @@ -42,7 +42,7 @@ object QrErrorMapper { is RustHumanQrLoginException.OtherDeviceNotSignedIn -> QrLoginException.OtherDeviceNotSignedIn is RustHumanQrLoginException.LinkingNotSupported -> QrLoginException.LinkingNotSupported is RustHumanQrLoginException.Unknown -> QrLoginException.Unknown - is RustHumanQrLoginException.OAuthMetadataInvalid -> QrLoginException.OidcMetadataInvalid + is RustHumanQrLoginException.OAuthMetadataInvalid -> QrLoginException.OAuthMetadataInvalid is RustHumanQrLoginException.SlidingSyncNotAvailable -> QrLoginException.SlidingSyncNotAvailable is RustHumanQrLoginException.CheckCodeAlreadySent -> QrLoginException.CheckCodeAlreadySent is RustHumanQrLoginException.CheckCodeCannotBeSent -> QrLoginException.CheckCodeCannotBeSent diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt index 8a49d1b11b..7ee9c7d0b3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustIdentityResetHandle.kt @@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.impl.encryption import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle +import io.element.android.libraries.matrix.api.encryption.IdentityOAuthResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle import org.matrix.rustcomponents.sdk.AuthData @@ -25,7 +25,7 @@ object RustIdentityResetHandleFactory { return runCatchingExceptions { identityResetHandle?.let { when (val authType = identityResetHandle.authType()) { - is CrossSigningResetAuthType.OAuth -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) + is CrossSigningResetAuthType.OAuth -> RustIdentityOAuthResetHandle(identityResetHandle, authType.info.approvalUrl) // User interactive authentication (user + password) CrossSigningResetAuthType.Uiaa -> RustPasswordIdentityResetHandle(userId, identityResetHandle) } @@ -47,11 +47,11 @@ class RustPasswordIdentityResetHandle( } } -class RustOidcIdentityResetHandle( +class RustIdentityOAuthResetHandle( private val identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle, override val url: String, -) : IdentityOidcResetHandle { - override suspend fun resetOidc(): Result { +) : IdentityOAuthResetHandle { + override suspend fun resetOAuth(): Result { return runCatchingExceptions { identityResetHandle.reset(null) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt index bfb200a994..dfda37d7e1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/mapper/Session.kt @@ -27,7 +27,7 @@ internal fun Session.toSessionData( accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = homeserverUrl ?: this.homeserverUrl, - oidcData = oauthData, + oAuthData = oauthData, loginTimestamp = Date(), isTokenValid = isTokenValid, loginType = loginType, @@ -52,7 +52,7 @@ internal fun ExternalSession.toSessionData( accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = homeserverUrl, - oidcData = null, + oAuthData = null, loginTimestamp = Date(), isTokenValid = isTokenValid, loginType = loginType, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementAction.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementAction.kt similarity index 86% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementAction.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementAction.kt index f86c57543a..974ae08923 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementAction.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementAction.kt @@ -6,9 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.oidc +package io.element.android.libraries.matrix.impl.oauth -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import org.matrix.rustcomponents.sdk.AccountManagementAction as RustAccountManagementAction fun AccountManagementAction.toRustAction(): RustAccountManagementAction { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt index 7cfd3392a1..cc5fb4a394 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationExceptionMappingTest.kt @@ -64,17 +64,17 @@ class AuthenticationExceptionMappingTest { } @Test - fun `mapping Oidc exceptions map to the Oidc Kotlin`() { + fun `mapping Oidc exceptions map to the OAuth Kotlin`() { assertThat(OAuthException.Generic("Generic").mapAuthenticationException()) - .isException("Generic") + .isException("Generic") assertThat(OAuthException.CallbackUrlInvalid("CallbackUrlInvalid").mapAuthenticationException()) - .isException("CallbackUrlInvalid") + .isException("CallbackUrlInvalid") assertThat(OAuthException.Cancelled("Cancelled").mapAuthenticationException()) - .isException("Cancelled") + .isException("Cancelled") assertThat(OAuthException.MetadataInvalid("MetadataInvalid").mapAuthenticationException()) - .isException("MetadataInvalid") + .isException("MetadataInvalid") assertThat(OAuthException.NotSupported("NotSupported").mapAuthenticationException()) - .isException("NotSupported") + .isException("NotSupported") } private inline fun ThrowableSubject.isException(message: String) { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetailsKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetailsKtTest.kt index a5c7b2dbc8..63f573536c 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetailsKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetailsKtTest.kt @@ -20,7 +20,7 @@ class HomeserverDetailsKtTest { val homeserverLoginDetails = FakeFfiHomeserverLoginDetails( url = "https://example.org", supportsPasswordLogin = true, - supportsOidcLogin = false + supportsOAuthLogin = false ) // When @@ -31,7 +31,7 @@ class HomeserverDetailsKtTest { MatrixHomeServerDetails( url = "https://example.org", supportsPasswordLogin = true, - supportsOidcLogin = false + supportsOAuthLogin = false ) ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProviderTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProviderTest.kt similarity index 76% rename from libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProviderTest.kt rename to libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProviderTest.kt index 095cf54946..3b54f2d1f3 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfigurationProviderTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/OAuthConfigurationProviderTest.kt @@ -10,18 +10,18 @@ package io.element.android.libraries.matrix.impl.auth import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.test.auth.FAKE_REDIRECT_URL -import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider +import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider import io.element.android.libraries.matrix.test.core.aBuildMeta import org.junit.Test -class OidcConfigurationProviderTest { +class OAuthConfigurationProviderTest { @Test fun get() { - val result = OidcConfigurationProvider( + val result = OAuthConfigurationProvider( buildMeta = aBuildMeta( applicationName = "myName", ), - oidcRedirectUrlProvider = FakeOidcRedirectUrlProvider(), + oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), ).get() assertThat(result.clientName).isEqualTo("myName") assertThat(result.redirectUri).isEqualTo(FAKE_REDIRECT_URL) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt index 50d1f3723b..903273113b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt @@ -18,8 +18,8 @@ import org.junit.Test class RustHomeserverLoginCompatibilityCheckerTest { @Test - fun `check - is valid if it supports OIDC login`() = runTest { - val sut = createChecker { FakeFfiHomeserverLoginDetails(supportsOidcLogin = true) } + fun `check - is valid if it supports OAuth login`() = runTest { + val sut = createChecker { FakeFfiHomeserverLoginDetails(supportsOAuthLogin = true) } assertThat(sut.check("https://matrix.host.org").getOrNull()).isTrue() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt index f4ce7b1fdd..bff2711bc5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt @@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClientBuilder import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiHomeserverLoginDetails import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory -import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider +import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore @@ -64,9 +64,9 @@ class RustMatrixAuthenticationServiceTest { sessionStore = sessionStore, rustMatrixClientFactory = rustMatrixClientFactory, passphraseGenerator = FakePassphraseGenerator(), - oidcConfigurationProvider = OidcConfigurationProvider( + oAuthConfigurationProvider = OAuthConfigurationProvider( buildMeta = aBuildMeta(), - oidcRedirectUrlProvider = FakeOidcRedirectUrlProvider(), + oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), ), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt index c20a77ace4..4de5e55985 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/qrlogin/QrErrorMapperTest.kt @@ -32,7 +32,7 @@ class QrErrorMapperTest { assertThat(QrErrorMapper.map(RustHumanQrLoginException.OtherDeviceNotSignedIn())).isEqualTo(QrLoginException.OtherDeviceNotSignedIn) assertThat(QrErrorMapper.map(RustHumanQrLoginException.LinkingNotSupported())).isEqualTo(QrLoginException.LinkingNotSupported) assertThat(QrErrorMapper.map(RustHumanQrLoginException.Unknown())).isEqualTo(QrLoginException.Unknown) - assertThat(QrErrorMapper.map(RustHumanQrLoginException.OAuthMetadataInvalid())).isEqualTo(QrLoginException.OidcMetadataInvalid) + assertThat(QrErrorMapper.map(RustHumanQrLoginException.OAuthMetadataInvalid())).isEqualTo(QrLoginException.OAuthMetadataInvalid) assertThat(QrErrorMapper.map(RustHumanQrLoginException.SlidingSyncNotAvailable())).isEqualTo(QrLoginException.SlidingSyncNotAvailable) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt index 65e24c2494..5351fbc8df 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt @@ -14,11 +14,11 @@ import org.matrix.rustcomponents.sdk.NoHandle class FakeFfiHomeserverLoginDetails( private val url: String = "https://example.org", private val supportsPasswordLogin: Boolean = false, - private val supportsOidcLogin: Boolean = false, + private val supportsOAuthLogin: Boolean = false, private val supportsSsoLogin: Boolean = false, ) : HomeserverLoginDetails(NoHandle) { override fun url(): String = url - override fun supportsOauthLogin(): Boolean = supportsOidcLogin + override fun supportsOauthLogin(): Boolean = supportsOAuthLogin override fun supportsPasswordLogin(): Boolean = supportsPasswordLogin override fun supportsSsoLogin(): Boolean = supportsSsoLogin } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/SessionKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/SessionKtTest.kt index e5fc8b154f..fb9ec6625b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/SessionKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/mapper/SessionKtTest.kt @@ -36,7 +36,7 @@ class SessionKtTest { assertThat(result.refreshToken).isEqualTo("refreshToken") assertThat(result.homeserverUrl).isEqualTo(A_HOMESERVER_URL) assertThat(result.isTokenValid).isTrue() - assertThat(result.oidcData).isNull() + assertThat(result.oAuthData).isNull() assertThat(result.loginType).isEqualTo(LoginType.PASSWORD) assertThat(result.loginTimestamp).isNotNull() assertThat(result.passphrase).isEqualTo(A_SECRET) @@ -82,7 +82,7 @@ class SessionKtTest { assertThat(result.refreshToken).isNull() assertThat(result.homeserverUrl).isEqualTo(A_HOMESERVER_URL) assertThat(result.isTokenValid).isTrue() - assertThat(result.oidcData).isNull() + assertThat(result.oAuthData).isNull() assertThat(result.loginType).isEqualTo(LoginType.PASSWORD) assertThat(result.loginTimestamp).isNotNull() assertThat(result.passphrase).isEqualTo(A_SECRET) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementActionKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementActionKtTest.kt similarity index 90% rename from libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementActionKtTest.kt rename to libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementActionKtTest.kt index 3637ef78cc..1495716e87 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oidc/AccountManagementActionKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/oauth/AccountManagementActionKtTest.kt @@ -6,10 +6,10 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl.oidc +package io.element.android.libraries.matrix.impl.oauth import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.test.A_DEVICE_ID import org.junit.Test import org.matrix.rustcomponents.sdk.AccountManagementAction as RustAccountManagementAction 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 742af160ae..26bc0af8c1 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 @@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaPreviewService import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService -import io.element.android.libraries.matrix.api.oidc.AccountManagementAction +import io.element.android.libraries.matrix.api.oauth.AccountManagementAction import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.JoinedRoom 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 238ad2663d..3b2fdeafb1 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 @@ -12,8 +12,8 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.ElementClassicSession import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails -import io.element.android.libraries.matrix.api.auth.OidcDetails -import io.element.android.libraries.matrix.api.auth.OidcPrompt +import io.element.android.libraries.matrix.api.auth.OAuthDetails +import io.element.android.libraries.matrix.api.auth.OAuthPrompt import io.element.android.libraries.matrix.api.auth.external.ExternalSession import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep @@ -26,7 +26,7 @@ import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.simulateLongTask -val A_OIDC_DATA = OidcDetails(url = "a-url") +val AN_OAUTH_DATA = OAuthDetails(url = "a-url") class FakeMatrixAuthenticationService( var matrixClientResult: ((SessionId) -> Result)? = null, @@ -37,8 +37,8 @@ class FakeMatrixAuthenticationService( private val setElementClassicSessionResult: (ElementClassicSession?) -> Unit = { lambdaError() }, private val doSecretsContainBackupKeyResult: (UserId, String, String) -> Boolean = { _, _, _ -> lambdaError() }, ) : MatrixAuthenticationService { - private var oidcError: Throwable? = null - private var oidcCancelError: Throwable? = null + private var oAuthError: Throwable? = null + private var oAuthCancelError: Throwable? = null private var loginError: Throwable? = null private var matrixClient: MatrixClient? = null private var onAuthenticationListener: ((MatrixClient) -> Unit)? = null @@ -70,18 +70,18 @@ class FakeMatrixAuthenticationService( return importCreatedSessionLambda(externalSession) } - override suspend fun getOidcUrl( - prompt: OidcPrompt, + override suspend fun getOAuthUrl( + prompt: OAuthPrompt, loginHint: String?, - ): Result = simulateLongTask { - oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA) + ): Result = simulateLongTask { + oAuthError?.let { Result.failure(it) } ?: Result.success(AN_OAUTH_DATA) } - override suspend fun cancelOidcLogin(): Result { - return oidcCancelError?.let { Result.failure(it) } ?: Result.success(Unit) + override suspend fun cancelOAuthLogin(): Result { + return oAuthCancelError?.let { Result.failure(it) } ?: Result.success(Unit) } - override suspend fun loginWithOidc(callbackUrl: String): Result = simulateLongTask { + override suspend fun loginWithOAuth(callbackUrl: String): Result = simulateLongTask { loginError?.let { Result.failure(it) } ?: run { onAuthenticationListener?.invoke(matrixClient ?: FakeMatrixClient()) Result.success(A_USER_ID) @@ -97,12 +97,12 @@ class FakeMatrixAuthenticationService( onAuthenticationListener = lambda } - fun givenOidcError(throwable: Throwable?) { - oidcError = throwable + fun givenOAuthError(throwable: Throwable?) { + oAuthError = throwable } - fun givenOidcCancelError(throwable: Throwable?) { - oidcCancelError = throwable + fun givenOAuthCancelError(throwable: Throwable?) { + oAuthCancelError = throwable } fun givenLoginError(throwable: Throwable?) { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOidcRedirectUrlProvider.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOAuthRedirectUrlProvider.kt similarity index 75% rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOidcRedirectUrlProvider.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOAuthRedirectUrlProvider.kt index 47c9b0951d..fcb28b48ba 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOidcRedirectUrlProvider.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeOAuthRedirectUrlProvider.kt @@ -8,12 +8,12 @@ package io.element.android.libraries.matrix.test.auth -import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider +import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider const val FAKE_REDIRECT_URL = "io.element.android:/" -class FakeOidcRedirectUrlProvider( +class FakeOAuthRedirectUrlProvider( private val provideResult: String = FAKE_REDIRECT_URL, -) : OidcRedirectUrlProvider { +) : OAuthRedirectUrlProvider { override fun provide() = provideResult } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt index 3b9573bcfc..56fd4de4c3 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/MatrixHomeServerDetails.kt @@ -14,9 +14,9 @@ import io.element.android.libraries.matrix.test.A_HOMESERVER_URL fun aMatrixHomeServerDetails( url: String = A_HOMESERVER_URL, supportsPasswordLogin: Boolean = false, - supportsOidcLogin: Boolean = false, + supportsOAuthLogin: Boolean = false, ) = MatrixHomeServerDetails( url = url, supportsPasswordLogin = supportsPasswordLogin, - supportsOidcLogin = supportsOidcLogin, + supportsOAuthLogin = supportsOAuthLogin, ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeIdentityResetHandle.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeIdentityResetHandle.kt index 06ffeb547c..d65328d7fe 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeIdentityResetHandle.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeIdentityResetHandle.kt @@ -8,16 +8,16 @@ package io.element.android.libraries.matrix.test.encryption -import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle +import io.element.android.libraries.matrix.api.encryption.IdentityOAuthResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle -class FakeIdentityOidcResetHandle( +class FakeIdentityOAuthResetHandle( override val url: String = "", - var resetOidcLambda: () -> Result = { error("Not implemented") }, + var resetOAuthLambda: () -> Result = { error("Not implemented") }, var cancelLambda: () -> Unit = { error("Not implemented") }, -) : IdentityOidcResetHandle { - override suspend fun resetOidc(): Result { - return resetOidcLambda() +) : IdentityOAuthResetHandle { + override suspend fun resetOAuth(): Result { + return resetOAuthLambda() } override suspend fun cancel() { diff --git a/libraries/oidc/api/build.gradle.kts b/libraries/oauth/api/build.gradle.kts similarity index 87% rename from libraries/oidc/api/build.gradle.kts rename to libraries/oauth/api/build.gradle.kts index 8cc0125142..8c863db0bf 100644 --- a/libraries/oidc/api/build.gradle.kts +++ b/libraries/oauth/api/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.libraries.oidc.api" + namespace = "io.element.android.libraries.oauth.api" } dependencies { diff --git a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthAction.kt similarity index 54% rename from libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt rename to libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthAction.kt index d7c061ab25..033516b93d 100644 --- a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcAction.kt +++ b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthAction.kt @@ -6,9 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.api +package io.element.android.libraries.oauth.api -sealed interface OidcAction { - data class GoBack(val toUnblock: Boolean = false) : OidcAction - data class Success(val url: String) : OidcAction +sealed interface OAuthAction { + data class GoBack(val toUnblock: Boolean = false) : OAuthAction + data class Success(val url: String) : OAuthAction } diff --git a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcActionFlow.kt b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt similarity index 63% rename from libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcActionFlow.kt rename to libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt index 17340eb5ec..c6791e3197 100644 --- a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcActionFlow.kt +++ b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthActionFlow.kt @@ -6,12 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.api +package io.element.android.libraries.oauth.api import kotlinx.coroutines.flow.FlowCollector -interface OidcActionFlow { - fun post(oidcAction: OidcAction) - suspend fun collect(collector: FlowCollector) +interface OAuthActionFlow { + fun post(oAuthAction: OAuthAction) + suspend fun collect(collector: FlowCollector) fun reset() } diff --git a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcIntentResolver.kt b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt similarity index 68% rename from libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcIntentResolver.kt rename to libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt index 97fa1baa27..2091a86db4 100644 --- a/libraries/oidc/api/src/main/kotlin/io/element/android/libraries/oidc/api/OidcIntentResolver.kt +++ b/libraries/oauth/api/src/main/kotlin/io/element/android/libraries/oauth/api/OAuthIntentResolver.kt @@ -6,10 +6,10 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.api +package io.element.android.libraries.oauth.api import android.content.Intent -interface OidcIntentResolver { - fun resolve(intent: Intent): OidcAction? +interface OAuthIntentResolver { + fun resolve(intent: Intent): OAuthAction? } diff --git a/libraries/oidc/impl/build.gradle.kts b/libraries/oauth/impl/build.gradle.kts similarity index 93% rename from libraries/oidc/impl/build.gradle.kts rename to libraries/oauth/impl/build.gradle.kts index e11ce11c70..d051c06497 100644 --- a/libraries/oidc/impl/build.gradle.kts +++ b/libraries/oauth/impl/build.gradle.kts @@ -16,7 +16,7 @@ plugins { } android { - namespace = "io.element.android.libraries.oidc.impl" + namespace = "io.element.android.libraries.oauth.impl" testOptions { unitTests { @@ -39,7 +39,7 @@ dependencies { implementation(platform(libs.network.retrofit.bom)) implementation(libs.network.retrofit) implementation(libs.serialization.json) - api(projects.libraries.oidc.api) + api(projects.libraries.oauth.api) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlow.kt b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlow.kt similarity index 58% rename from libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlow.kt rename to libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlow.kt index 6096ef7eef..6b23676059 100644 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlow.kt +++ b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlow.kt @@ -6,26 +6,26 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.impl +package io.element.android.libraries.oauth.impl import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.MutableStateFlow @ContributesBinding(AppScope::class) @SingleIn(AppScope::class) -class DefaultOidcActionFlow : OidcActionFlow { - private val mutableStateFlow = MutableStateFlow(null) +class DefaultOAuthActionFlow : OAuthActionFlow { + private val mutableStateFlow = MutableStateFlow(null) - override fun post(oidcAction: OidcAction) { - mutableStateFlow.value = oidcAction + override fun post(oAuthAction: OAuthAction) { + mutableStateFlow.value = oAuthAction } - override suspend fun collect(collector: FlowCollector) { + override suspend fun collect(collector: FlowCollector) { mutableStateFlow.collect(collector) } diff --git a/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolver.kt b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolver.kt new file mode 100644 index 0000000000..c2a29e228c --- /dev/null +++ b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolver.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2023-2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.oauth.impl + +import android.content.Intent +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthIntentResolver + +@ContributesBinding(AppScope::class) +class DefaultOAuthIntentResolver( + private val oAuthUrlParser: OAuthUrlParser, +) : OAuthIntentResolver { + override fun resolve(intent: Intent): OAuthAction? { + return oAuthUrlParser.parse(intent.dataString.orEmpty()) + } +} diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/OAuthUrlParser.kt similarity index 51% rename from libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt rename to libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/OAuthUrlParser.kt index 8933873dc2..e62dea696f 100644 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/OidcUrlParser.kt +++ b/libraries/oauth/impl/src/main/kotlin/io/element/android/libraries/oauth/impl/OAuthUrlParser.kt @@ -6,37 +6,37 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.impl +package io.element.android.libraries.oauth.impl import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider -import io.element.android.libraries.oidc.api.OidcAction +import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider +import io.element.android.libraries.oauth.api.OAuthAction -fun interface OidcUrlParser { - fun parse(url: String): OidcAction? +fun interface OAuthUrlParser { + fun parse(url: String): OAuthAction? } /** - * Simple parser for oidc url interception. + * Simple parser for OAuth url interception. * TODO Find documentation about the format. */ @ContributesBinding(AppScope::class) -class DefaultOidcUrlParser( - private val oidcRedirectUrlProvider: OidcRedirectUrlProvider, -) : OidcUrlParser { +class DefaultOAuthUrlParser( + private val oAuthRedirectUrlProvider: OAuthRedirectUrlProvider, +) : OAuthUrlParser { /** - * Return a OidcAction, or null if the url is not a OidcUrl. + * Return a [OAuthAction], or null if the url is not an OAuth url. * Note: * When user press button "Cancel", we get the url: * `io.element.android:/?error=access_denied&state=IFF1UETGye2ZA8pO` * On success, we get: * `io.element.android:/?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB` */ - override fun parse(url: String): OidcAction? { - if (url.startsWith(oidcRedirectUrlProvider.provide()).not()) return null - if (url.contains("error=access_denied")) return OidcAction.GoBack() - if (url.contains("code=")) return OidcAction.Success(url) + override fun parse(url: String): OAuthAction? { + if (url.startsWith(oAuthRedirectUrlProvider.provide()).not()) return null + if (url.contains("error=access_denied")) return OAuthAction.GoBack() + if (url.contains("code=")) return OAuthAction.Success(url) // Other case not supported, let's crash the app for now error("Not supported: $url") diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlowTest.kt similarity index 58% rename from libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt rename to libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlowTest.kt index 387b9aceb0..817487b3ff 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcActionFlowTest.kt +++ b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthActionFlowTest.kt @@ -1,34 +1,33 @@ /* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.impl +package io.element.android.libraries.oauth.impl import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.oidc.api.OidcAction +import io.element.android.libraries.oauth.api.OAuthAction import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.Test -class DefaultOidcActionFlowTest { +class DefaultOAuthActionFlowTest { @Test fun `collect gets all the posted events`() = runTest { - val data = mutableListOf() - val sut = DefaultOidcActionFlow() + val data = mutableListOf() + val sut = DefaultOAuthActionFlow() backgroundScope.launch { sut.collect { action -> data.add(action) } } - sut.post(OidcAction.GoBack()) + sut.post(OAuthAction.GoBack()) delay(1) sut.reset() delay(1) - assertThat(data).containsExactly(OidcAction.GoBack(), null) + assertThat(data).containsExactly(OAuthAction.GoBack(), null) } } diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolverTest.kt similarity index 64% rename from libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt rename to libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolverTest.kt index 64068030d7..ff34ae8220 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolverTest.kt +++ b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthIntentResolverTest.kt @@ -1,19 +1,18 @@ /* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.impl +package io.element.android.libraries.oauth.impl import android.app.Activity import android.content.Intent import androidx.core.net.toUri import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider -import io.element.android.libraries.oidc.api.OidcAction +import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider +import io.element.android.libraries.oauth.api.OAuthAction import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith @@ -21,36 +20,36 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment @RunWith(RobolectricTestRunner::class) -class DefaultOidcIntentResolverTest { +class DefaultOAuthIntentResolverTest { @Test - fun `test resolve oidc go back`() { - val sut = createDefaultOidcIntentResolver() + fun `test resolve OAuth go back`() { + val sut = createDefaultOAuthIntentResolver() val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW data = "io.element.android:/?error=access_denied&state=IFF1UETGye2ZA8pO".toUri() } val result = sut.resolve(intent) - assertThat(result).isEqualTo(OidcAction.GoBack()) + assertThat(result).isEqualTo(OAuthAction.GoBack()) } @Test - fun `test resolve oidc success`() { - val sut = createDefaultOidcIntentResolver() + fun `test resolve OAuth success`() { + val sut = createDefaultOAuthIntentResolver() val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW data = "io.element.android:/?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB".toUri() } val result = sut.resolve(intent) assertThat(result).isEqualTo( - OidcAction.Success( + OAuthAction.Success( url = "io.element.android:/?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" ) ) } @Test - fun `test resolve oidc invalid`() { - val sut = createDefaultOidcIntentResolver() + fun `test resolve OAuth invalid`() { + val sut = createDefaultOAuthIntentResolver() val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply { action = Intent.ACTION_VIEW data = "io.element.android:/invalid".toUri() @@ -60,10 +59,10 @@ class DefaultOidcIntentResolverTest { } } - private fun createDefaultOidcIntentResolver(): DefaultOidcIntentResolver { - return DefaultOidcIntentResolver( - oidcUrlParser = DefaultOidcUrlParser( - oidcRedirectUrlProvider = FakeOidcRedirectUrlProvider(), + private fun createDefaultOAuthIntentResolver(): DefaultOAuthIntentResolver { + return DefaultOAuthIntentResolver( + oAuthUrlParser = DefaultOAuthUrlParser( + oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), ), ) } diff --git a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthUrlParserTest.kt similarity index 56% rename from libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt rename to libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthUrlParserTest.kt index 7f145c053d..6377414862 100644 --- a/libraries/oidc/impl/src/test/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcUrlParserTest.kt +++ b/libraries/oauth/impl/src/test/kotlin/io/element/android/libraries/oauth/impl/DefaultOAuthUrlParserTest.kt @@ -1,59 +1,58 @@ /* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.impl +package io.element.android.libraries.oauth.impl import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.test.auth.FAKE_REDIRECT_URL -import io.element.android.libraries.matrix.test.auth.FakeOidcRedirectUrlProvider -import io.element.android.libraries.oidc.api.OidcAction +import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider +import io.element.android.libraries.oauth.api.OAuthAction import org.junit.Assert import org.junit.Test -class DefaultOidcUrlParserTest { +class DefaultOAuthUrlParserTest { @Test fun `test empty url`() { - val sut = createDefaultOidcUrlParser() + val sut = createDefaultOAuthUrlParser() assertThat(sut.parse("")).isNull() } @Test fun `test regular url`() { - val sut = createDefaultOidcUrlParser() + val sut = createDefaultOAuthUrlParser() assertThat(sut.parse("https://matrix.org")).isNull() } @Test fun `test cancel url`() { - val sut = createDefaultOidcUrlParser() + val sut = createDefaultOAuthUrlParser() val aCancelUrl = "$FAKE_REDIRECT_URL?error=access_denied&state=IFF1UETGye2ZA8pO" - assertThat(sut.parse(aCancelUrl)).isEqualTo(OidcAction.GoBack()) + assertThat(sut.parse(aCancelUrl)).isEqualTo(OAuthAction.GoBack()) } @Test fun `test success url`() { - val sut = createDefaultOidcUrlParser() + val sut = createDefaultOAuthUrlParser() val aSuccessUrl = "$FAKE_REDIRECT_URL?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" - assertThat(sut.parse(aSuccessUrl)).isEqualTo(OidcAction.Success(aSuccessUrl)) + assertThat(sut.parse(aSuccessUrl)).isEqualTo(OAuthAction.Success(aSuccessUrl)) } @Test fun `test unknown url`() { - val sut = createDefaultOidcUrlParser() + val sut = createDefaultOAuthUrlParser() val anUnknownUrl = "$FAKE_REDIRECT_URL?state=IFF1UETGye2ZA8pO&goat=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" Assert.assertThrows(IllegalStateException::class.java) { assertThat(sut.parse(anUnknownUrl)) } } - private fun createDefaultOidcUrlParser(): DefaultOidcUrlParser { - return DefaultOidcUrlParser( - oidcRedirectUrlProvider = FakeOidcRedirectUrlProvider(), + private fun createDefaultOAuthUrlParser(): DefaultOAuthUrlParser { + return DefaultOAuthUrlParser( + oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), ) } } diff --git a/libraries/oidc/test/build.gradle.kts b/libraries/oauth/test/build.gradle.kts similarity index 80% rename from libraries/oidc/test/build.gradle.kts rename to libraries/oauth/test/build.gradle.kts index efe32d404a..6850653ddc 100644 --- a/libraries/oidc/test/build.gradle.kts +++ b/libraries/oauth/test/build.gradle.kts @@ -11,11 +11,11 @@ plugins { } android { - namespace = "io.element.android.libraries.oidc.test" + namespace = "io.element.android.libraries.oauth.test" } dependencies { implementation(libs.coroutines.core) - api(projects.libraries.oidc.api) + api(projects.libraries.oauth.api) implementation(projects.tests.testutils) } diff --git a/libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/FakeOidcIntentResolver.kt b/libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/FakeOAuthIntentResolver.kt similarity index 50% rename from libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/FakeOidcIntentResolver.kt rename to libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/FakeOAuthIntentResolver.kt index 45b400868b..893289023e 100644 --- a/libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/FakeOidcIntentResolver.kt +++ b/libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/FakeOAuthIntentResolver.kt @@ -6,17 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.oidc.test +package io.element.android.libraries.oauth.test import android.content.Intent -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcIntentResolver +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthIntentResolver import io.element.android.tests.testutils.lambda.lambdaError -class FakeOidcIntentResolver( - private val resolveResult: (Intent) -> OidcAction? = { lambdaError() } -) : OidcIntentResolver { - override fun resolve(intent: Intent): OidcAction? { +class FakeOAuthIntentResolver( + private val resolveResult: (Intent) -> OAuthAction? = { lambdaError() } +) : OAuthIntentResolver { + override fun resolve(intent: Intent): OAuthAction? { return resolveResult(intent) } } diff --git a/libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/customtab/FakeOAuthActionFlow.kt b/libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/customtab/FakeOAuthActionFlow.kt new file mode 100644 index 0000000000..5a5ca4369e --- /dev/null +++ b/libraries/oauth/test/src/main/kotlin/io/element/android/libraries/oauth/test/customtab/FakeOAuthActionFlow.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.oauth.test.customtab + +import io.element.android.libraries.oauth.api.OAuthAction +import io.element.android.libraries.oauth.api.OAuthActionFlow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * This is actually a copy of DefaultOAuthActionFlow. + */ +class FakeOAuthActionFlow : OAuthActionFlow { + private val mutableStateFlow = MutableStateFlow(null) + + override fun post(oAuthAction: OAuthAction) { + mutableStateFlow.value = oAuthAction + } + + override suspend fun collect(collector: FlowCollector) { + mutableStateFlow.collect(collector) + } + + override fun reset() { + mutableStateFlow.value = null + } +} diff --git a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolver.kt b/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolver.kt deleted file mode 100644 index 2a16030b3b..0000000000 --- a/libraries/oidc/impl/src/main/kotlin/io/element/android/libraries/oidc/impl/DefaultOidcIntentResolver.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2023-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.oidc.impl - -import android.content.Intent -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcIntentResolver - -@ContributesBinding(AppScope::class) -class DefaultOidcIntentResolver( - private val oidcUrlParser: OidcUrlParser, -) : OidcIntentResolver { - override fun resolve(intent: Intent): OidcAction? { - return oidcUrlParser.parse(intent.dataString.orEmpty()) - } -} diff --git a/libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/customtab/FakeOidcActionFlow.kt b/libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/customtab/FakeOidcActionFlow.kt deleted file mode 100644 index 5362aefa7c..0000000000 --- a/libraries/oidc/test/src/main/kotlin/io/element/android/libraries/oidc/test/customtab/FakeOidcActionFlow.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.oidc.test.customtab - -import io.element.android.libraries.oidc.api.OidcAction -import io.element.android.libraries.oidc.api.OidcActionFlow -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.MutableStateFlow - -/** - * This is actually a copy of DefaultOidcActionFlow. - */ -class FakeOidcActionFlow : OidcActionFlow { - private val mutableStateFlow = MutableStateFlow(null) - - override fun post(oidcAction: OidcAction) { - mutableStateFlow.value = oidcAction - } - - override suspend fun collect(collector: FlowCollector) { - mutableStateFlow.collect(collector) - } - - override fun reset() { - mutableStateFlow.value = null - } -} diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt index 568dbe7e3a..88cf9558b3 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionData.kt @@ -24,8 +24,8 @@ data class SessionData( val refreshToken: String?, /** The homeserver URL of the session. */ val homeserverUrl: String, - /** The Open ID Connect info for this session, if any. */ - val oidcData: String?, + /** The Open Authorization info for this session, if any. */ + val oAuthData: String?, /** The timestamp of the last login. May be `null` in very old sessions. */ val loginTimestamp: Date?, /** Whether the [accessToken] is valid or not. */ diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt index ea69709bbd..316bf86c1c 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/SessionDataMapper.kt @@ -20,7 +20,7 @@ internal fun SessionData.toDbModel(): DbSessionData { accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = homeserverUrl, - oidcData = oidcData, + oidcData = oAuthData, loginTimestamp = loginTimestamp?.time, isTokenValid = if (isTokenValid) 1L else 0L, loginType = loginType.name, @@ -41,7 +41,7 @@ internal fun DbSessionData.toApiModel(): SessionData { accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = homeserverUrl, - oidcData = oidcData, + oAuthData = oidcData, loginTimestamp = loginTimestamp?.let { Date(it) }, isTokenValid = isTokenValid == 1L, loginType = LoginType.fromName(loginType ?: LoginType.UNKNOWN.name), diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt index c791a20620..c5acd77755 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/SessionData.kt @@ -30,7 +30,7 @@ fun aSessionData( accessToken = accessToken, refreshToken = refreshToken, homeserverUrl = "aHomeserverUrl", - oidcData = null, + oAuthData = null, loginTimestamp = null, isTokenValid = isTokenValid, loginType = LoginType.UNKNOWN, diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index ce5c324ff4..f832136083 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -120,7 +120,7 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:troubleshoot:impl")) implementation(project(":libraries:fullscreenintent:impl")) implementation(project(":libraries:wellknown:impl")) - implementation(project(":libraries:oidc:impl")) + implementation(project(":libraries:oauth:impl")) implementation(project(":libraries:workmanager:impl")) implementation(project(":libraries:recentemojis:impl")) } From 6de6e13f259dd3a2592ca6dc9fc3cbc1e47a8b4a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 29 Apr 2026 10:06:55 +0000 Subject: [PATCH 024/471] Update screenshots --- ...mpl.screens.confirmation_CodeConfirmationView_Day_0_en.png | 4 ++-- ...l.screens.confirmation_CodeConfirmationView_Night_0_en.png | 4 ++-- ...es.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png | 4 ++-- ...es.linknewdevice.impl.screens.error_ErrorView_Day_4_en.png | 4 ++-- ...es.linknewdevice.impl.screens.error_ErrorView_Day_6_en.png | 4 ++-- ...es.linknewdevice.impl.screens.error_ErrorView_Day_7_en.png | 4 ++-- ...es.linknewdevice.impl.screens.error_ErrorView_Day_8_en.png | 3 +++ ....linknewdevice.impl.screens.error_ErrorView_Night_3_en.png | 4 ++-- ....linknewdevice.impl.screens.error_ErrorView_Night_4_en.png | 4 ++-- ....linknewdevice.impl.screens.error_ErrorView_Night_6_en.png | 4 ++-- ....linknewdevice.impl.screens.error_ErrorView_Night_7_en.png | 4 ++-- ....linknewdevice.impl.screens.error_ErrorView_Night_8_en.png | 3 +++ ...newdevice.impl.screens.number_EnterNumberView_Day_0_en.png | 4 ++-- ...newdevice.impl.screens.number_EnterNumberView_Day_1_en.png | 4 ++-- ...newdevice.impl.screens.number_EnterNumberView_Day_2_en.png | 4 ++-- ...newdevice.impl.screens.number_EnterNumberView_Day_3_en.png | 4 ++-- ...newdevice.impl.screens.number_EnterNumberView_Day_4_en.png | 4 ++-- ...newdevice.impl.screens.number_EnterNumberView_Day_5_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_0_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_1_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_2_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_3_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_4_en.png | 4 ++-- ...wdevice.impl.screens.number_EnterNumberView_Night_5_en.png | 4 ++-- ...evice.impl.screens.root_LinkNewDeviceRootView_Day_0_en.png | 4 ++-- ...evice.impl.screens.root_LinkNewDeviceRootView_Day_1_en.png | 4 ++-- ...evice.impl.screens.root_LinkNewDeviceRootView_Day_3_en.png | 4 ++-- ...evice.impl.screens.root_LinkNewDeviceRootView_Day_4_en.png | 4 ++-- ...evice.impl.screens.root_LinkNewDeviceRootView_Day_5_en.png | 4 ++-- ...ice.impl.screens.root_LinkNewDeviceRootView_Night_0_en.png | 4 ++-- ...ice.impl.screens.root_LinkNewDeviceRootView_Night_1_en.png | 4 ++-- ...ice.impl.screens.root_LinkNewDeviceRootView_Night_3_en.png | 4 ++-- ...ice.impl.screens.root_LinkNewDeviceRootView_Night_4_en.png | 4 ++-- ...ice.impl.screens.root_LinkNewDeviceRootView_Night_5_en.png | 4 ++-- ...classic.missingkeybackup_MissingKeyBackupView_Day_0_en.png | 4 ++-- ...assic.missingkeybackup_MissingKeyBackupView_Night_0_en.png | 4 ++-- ...ns.qrcode.confirmation_QrCodeConfirmationView_Day_0_en.png | 4 ++-- ...ns.qrcode.confirmation_QrCodeConfirmationView_Day_1_en.png | 4 ++-- ...ns.qrcode.confirmation_QrCodeConfirmationView_Day_2_en.png | 4 ++-- ....qrcode.confirmation_QrCodeConfirmationView_Night_0_en.png | 4 ++-- ....qrcode.confirmation_QrCodeConfirmationView_Night_1_en.png | 4 ++-- ....qrcode.confirmation_QrCodeConfirmationView_Night_2_en.png | 4 ++-- ...gin.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en.png | 4 ++-- ...gin.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en.png | 4 ++-- ...gin.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en.png | 4 ++-- ...n.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en.png | 4 ++-- ...n.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en.png | 4 ++-- ...n.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en.png | 4 ++-- ...gin.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en.png | 4 ++-- ...gin.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en.png | 4 ++-- ...n.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en.png | 4 ++-- ...n.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_10_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_11_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_1_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_2_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_3_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_7_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_8_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Day_9_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_10_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_11_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_1_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_2_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_3_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_7_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_8_en.png | 4 ++-- .../images/features.logout.impl_LogoutView_Night_9_en.png | 4 ++-- ...impl.reset.password_ResetIdentityPasswordView_Day_0_en.png | 4 ++-- ...impl.reset.password_ResetIdentityPasswordView_Day_1_en.png | 4 ++-- ...impl.reset.password_ResetIdentityPasswordView_Day_2_en.png | 4 ++-- ...impl.reset.password_ResetIdentityPasswordView_Day_3_en.png | 4 ++-- ...pl.reset.password_ResetIdentityPasswordView_Night_0_en.png | 4 ++-- ...pl.reset.password_ResetIdentityPasswordView_Night_1_en.png | 4 ++-- ...pl.reset.password_ResetIdentityPasswordView_Night_2_en.png | 4 ++-- ...pl.reset.password_ResetIdentityPasswordView_Night_3_en.png | 4 ++-- ...backup.impl.setup_SecureBackupSetupViewChange_Day_0_en.png | 4 ++-- ...backup.impl.setup_SecureBackupSetupViewChange_Day_1_en.png | 4 ++-- ...backup.impl.setup_SecureBackupSetupViewChange_Day_5_en.png | 4 ++-- ...ckup.impl.setup_SecureBackupSetupViewChange_Night_0_en.png | 4 ++-- ...ckup.impl.setup_SecureBackupSetupViewChange_Night_1_en.png | 4 ++-- ...ckup.impl.setup_SecureBackupSetupViewChange_Night_5_en.png | 4 ++-- ...securebackup.impl.setup_SecureBackupSetupView_Day_0_en.png | 4 ++-- ...securebackup.impl.setup_SecureBackupSetupView_Day_1_en.png | 4 ++-- ...securebackup.impl.setup_SecureBackupSetupView_Day_5_en.png | 4 ++-- ...curebackup.impl.setup_SecureBackupSetupView_Night_0_en.png | 4 ++-- ...curebackup.impl.setup_SecureBackupSetupView_Night_1_en.png | 4 ++-- ...curebackup.impl.setup_SecureBackupSetupView_Night_5_en.png | 4 ++-- 88 files changed, 178 insertions(+), 172 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png index a9e31653f6..18b186ce98 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8dab1dc964cea9a76dc7130d8ac2bfe5d3c866fd6d8d969101eb50e828775d3 -size 31915 +oid sha256:23d54e777c8c9ade84bec155e3aef42f5b1c13b98a23ea6ecbd956d0090b5ad3 +size 32186 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png index 43c472ebb1..d1698e913f 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec67e3fef25c57331cb2425f8fc46a733e16a98c9945d0a4e5b950843c42fa34 -size 31087 +oid sha256:4bde248827b1c53b66c5ea7112af24cac43aea378266e3d7b7ad8a5fa2047258 +size 31261 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png index 3832b8d087..19a824127e 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe92b35b0fe2e5481eeb497f2ada1a639cb1a66f937b4245798edffea75b1f5c -size 35932 +oid sha256:d30e200c3e73775ed7081d3fbdc9f8843c5a5bf9e5f1b9ab3535cc6f0ea7f1f6 +size 35946 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_4_en.png index 4821d3ab76..292e8f0ead 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f60e02e48d483fe8d6b2bdf46192ccc8911fad1e79c7ac1e0272824cc04d3d1c -size 35648 +oid sha256:90e602edde377f866071ec78f391bb7cd0d9b59bec79be91d6a42108f604a32d +size 35833 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_6_en.png index 720026f561..6c9c87f774 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60333d74c8d940b32621defc5cdb3c3776b2980c137244dd34304365ee9ff447 -size 24158 +oid sha256:ced04df939214952c513aa8b4ef4c7a02dfe3bb3bce81f9ec6ea1827f639bf3f +size 24471 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_7_en.png index 0fcd59d198..429daba97a 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bec4c7d06dc2cc287d2165f588c6764e5314287119960947d7e24dbc19ce901 -size 23971 +oid sha256:018e6226239c439fc683eead0acb733d1e06eebad54b1a6c2b43d8cbd2e822cc +size 24324 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_8_en.png new file mode 100644 index 0000000000..14a6d77fe9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94e0cec5dac39083da711bdd30762eb84c794ecc41ab74f6dbe8cf5053402ea1 +size 22080 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png index 368e908499..f1f3896ab8 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f6bc4bb2233067ab6d07e596f8d5ca294501a47a21451a1115aa46e238b31a5 -size 34996 +oid sha256:a78b0677d896de960ce8ce7c5818b1d52004c91917c73c460a605e5e1342554f +size 35129 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_4_en.png index c9d622683c..51d6a1bc13 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:766e65cfceb2e9ff3895ecd5c2be2b80d7b2833b30a5c67a0075ef823bb77cc0 -size 34816 +oid sha256:31b97291b16fd0872891ad2341d95d34d8c0608f92e7a5e39b5f27d40769b111 +size 34955 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_6_en.png index ab0b09c575..3bd50c5655 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd50a4cb9848602b08b853f4fa36314b981a858b98349d68decfcbd6f402d719 -size 23631 +oid sha256:43cf679fbb9a83f14209e61129afb8e7f18a60f9bc127bfcf4f5dff05c79598e +size 24033 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_7_en.png index 4194458de9..2a85b2cf7f 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e2de9b362092dbd08deadac70591d4682304c69981fe27cbbeae4f26233d4aa -size 23444 +oid sha256:7ee6f9fbf05261efe52c247da3869c68c6e87f5132c52da72e56cf833ea6864d +size 23833 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_8_en.png new file mode 100644 index 0000000000..d36887d3e5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:144ea7ed8bba7d5ded34b5e00d7f3f036c4dc882be0cf8a644817ffee4c531f7 +size 21429 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en.png index 136812ccab..474a2b43a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11a877e3e4810777b7ddb15fa11b8410976142ee4fb5e9d4103f4248acf121cf -size 29685 +oid sha256:cc0409c3e6c60994c81d83c429bbfb7ebc4ee53ac4114d1913b089a8068ca51e +size 30046 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en.png index e04f62d6aa..fab5d228a6 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:471129545a85e702e8b59391f47630f128f80e819b042e91ee356684e4ae2438 -size 29680 +oid sha256:3670e0f777491b7f6101c3c1c3cc006e4848a8f6e481b9740fb42ee8807ccf9e +size 30024 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en.png index e5f8926345..764cb92eef 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a932c055ae0704cd62358c5442658c1cb059d7d3c0c56518de3e763c8eab3c52 -size 30355 +oid sha256:79b7dd684dcec4a06271eed34794e7546ba441ad2cc9796641ba570871e45cd3 +size 30659 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en.png index a1d475ecdd..9a795c080c 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ca49fb335880670a3b71fdd1f2e2be50e9a5a6677d2e17e9534c9724d9d55b1 -size 30534 +oid sha256:29233a6bad671608f5e1a1d20b24b454cfd70f8a846e4d9572cc86b8c7da45a0 +size 30843 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en.png index 116337e4be..58ae956b13 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be4f85040190c658d5734000e602b4cb43cb0a5ab2e2cea0a993959702c4722f -size 33681 +oid sha256:5d30ae8d9d3b3820dc17d7aff3bd5c6794f5a267993a5dae4dc601278ccaaa5c +size 33999 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en.png index bf1561d23e..7ba887d858 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29d7aac2eaef7850cc9b0f5583d6c9a8aef230525d40011a9a7cb5401b681d03 -size 33409 +oid sha256:bf5b6e85e57aef0d52253dc7563bbe0aae3f38e4a65e2fdc8e0460bccf26b006 +size 33722 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en.png index 99c888caba..a0404e5e72 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cb49a80d6ff771484743be2efc4900d053ccf84372614b98e9284d66d19056f -size 28895 +oid sha256:e0c446ba7fc8e0c0c68732ba34058b41e04d8641dcdbc83255f9417706044c8c +size 29067 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en.png index 4415136e95..fedf612386 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68845451924a562238f2c75dced10697cae75171d976e19c0de927eacfdc3210 -size 28853 +oid sha256:da2778e384c9f749e888690660f496b4af93b7cff6f93c9fef346580a477e1cf +size 29029 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en.png index 13df6a6877..6faf2d1cba 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cc21d3fe55a5dba7b9e70d445877b07f0c88bee61fbd64f929a2321473ea8ad -size 29581 +oid sha256:62c79d08701dad73ab3f5f7744cd2683febeb34cb8bab986685e0cd8bd33c0f6 +size 29773 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en.png index 5e08ee2145..d4c10cf76f 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf6312a9e19892c9db949474cb9aa3b7572cc132d3d5474278abb3c4d91b7ab1 -size 29505 +oid sha256:fad2e4dbc822c79fbd8569dd8db7a5c08b335793aff6923129175bd4479cb218 +size 29744 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en.png index e84f38285e..ff882fdaea 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f24e410e2715019c5fbba40a2f79750781e8db0f4b36c772cb7b37f0d3b0d26f -size 32697 +oid sha256:7a85f79b0167efdc0277d05a41712c39d10115482242bb84a140c6647c28d85d +size 32895 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en.png index 16f85bba9f..1fbbba121f 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd1e04c468c0daae10917f8e66a8e5a6ef8f72fa02f3ad4ca507b714dae7898d -size 32449 +oid sha256:865d4c7ff07834027411a76767aaaa8aa0febf529b910eaa51e5de9bd8a776fa +size 32641 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en.png index d6faf7dd8f..fb88b5b2a8 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b4a99e2ea65816e35ec8fb1cf40d8aafebb6fd5ae688911d7d009dda15cd0f9 -size 18123 +oid sha256:0ec014c00a19fb280f4e2047d873f1698eedde51a765948bd2652079921588a7 +size 18264 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en.png index db46722b7b..9bf1f8609c 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfbe2c73c732d671278ed411d76915d04d6e7ffce130c2a6dbe30ea43628e231 -size 24247 +oid sha256:ba8f1717d752a93cdda51a6ee5e8bac043a4572aadc4d1c0dba2bfe80e6c55db +size 24372 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en.png index c0703e447a..139ae28bda 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9168cc32d47f15de0ce961d841d872181b8aadf5151d4b5fd4d305c87cdda75 -size 21597 +oid sha256:7bc87f28234d11c58f5b43a7f87ee4e5a5d631e5a41ba11756c057a2a5eea1ff +size 21745 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en.png index 457e334ac2..a36301f230 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96448542138570c8615e24dd3b26be55145570c64ac9d410bbc4dac36280c345 -size 23769 +oid sha256:c97be1636f039afaab65c1b6858c439fcf47e6643681a886fd9c24ab6c7b0901 +size 23901 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en.png index 4621b6c95e..78e7e0d1bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:672e617ab62c72127a4c57bed6da8ba4710cfc24ca455ea6edc765d7a424a0f3 -size 33766 +oid sha256:90ca0d9059538259cd19e7e75f6adc11f21b3a1cc77aec8d5efaf3d55abf8505 +size 34006 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en.png index 52e64e87c3..e6153a4a2d 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:451565c6881885b51de299c2d71e77f78a62e68f96a75bc859050e64b87ef79e -size 17372 +oid sha256:1cd2061366439af2139a1ace15557ecdd8aa91c0d2f1e0adfc76a7d63e17133c +size 17615 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en.png index 5610b70d00..03c7fd3b0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:def1de8d3a76255e72e0d65dbbf6364f77c2764e2ac99b32a0145174cf0f3a28 -size 23244 +oid sha256:541b4cd380fee2f2a445fe25a1171492c865b10980dd79de70f42b0f3dc120c4 +size 23475 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en.png index 3784bbcac7..3cc9326747 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bd9567f7a504590da66993a2da6771c1a4157bc37301510f6d91d62efed7c16 -size 20745 +oid sha256:b71e2ba137ec1ffc65c07fc836074f7fa0ef310fb81a15db905db819ea29dd0e +size 20980 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en.png index e511df48c7..d6bb6bcc8d 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d662774653bcec6246ac6529ec98bcff77b02e4a6a8ef42ed0a640fed3770dff -size 22698 +oid sha256:de97697d988d0cc25626c7e31f153a7dca6987215edbe2e42ec16367ea9b4b3c +size 22952 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en.png index d35cdd7397..59789476b1 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee6a0cf330b30e4fdc7cb8b4268251850f2f112e1ad0ce4af1166341f04a0638 -size 32103 +oid sha256:9b3a92c98a93b1d9e7f127a13c664c7275e26030d59a0ebcd3641428d9a21683 +size 32303 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en.png index 47145ebd49..3314252e26 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62df5826abbcd47bc2a18743f2baeef8c9c85ebc547660a9ae92328cd49499b0 -size 62768 +oid sha256:6e97cab70b9ee3e870154ad4ae4cf1bf0bb413facf8253f5f9fe9429eddf76b1 +size 60303 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en.png index fa68b6c988..d58e079ced 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c92a6ada8c2aa5991c28935dafd8e35a0c0efc2c40660c16105b3cf381914cdd -size 61097 +oid sha256:caaa2a0f4de455704a6b5c78e19c2f723be7deaeec470c77ff1e87df194dd95c +size 58620 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en.png index 878e97a06e..048c9beb26 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbcf2216fe8f2653d44df345c157eb66f54ff8767c210bfeab5719d6d70230a8 -size 31686 +oid sha256:647163b31578572c9bf4a63a1d110f732fc3b0c41c3991651996608d6265c536 +size 31960 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en.png index 05edb58b0f..d27a10bf39 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:324b1392f40bb408b6469e656e774f5b1f7cbd9bb6557ec7a2587a971b548ccc -size 31288 +oid sha256:6e447151f2a7aaf9052130d5b915e9aea8180880c66fa51afb06d212079a0ced +size 31159 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en.png index 096b521d75..4b7e849116 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1075e98dac6a50a282a39dfd5855833080becd377e1bf3f9db8fb20104bda80d -size 33479 +oid sha256:e215ffdf8ba90e3f7a047d25206dfef4a08d25137ee2e4d3e5c16f254282927c +size 33371 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en.png index e738c998a3..276d9919f7 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f55c2e968f464ba9593dc8841a0e633c2f0a75cb3f85857aa3b4b716770629e -size 30831 +oid sha256:76613fd85c3211bd2c96e50f2d481d2d994aad20368713c1da309d0147ce1101 +size 31015 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en.png index ae7ffe2dd4..c1da84be8e 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8484c5300a8da5b120a24513b29bdd697dd3edd7d6b7c91e5925aa55c6290fd -size 30366 +oid sha256:5f32133324f20fc97c3473ca6ea3b1705d3b0beab5bed11af9199e4f5b9ab124 +size 30410 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en.png index 1cf5572c4e..e06e5ccaaa 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b87fe16a96a8c34b49aab481f65c3223fd098360048b7dcd1ee8bef85fdf378 -size 32650 +oid sha256:00556b6c310f63b548bc1a88b3b882a93412a2a7dacff008862faeb2ad572eb3 +size 32671 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en.png index 98039ad35b..d13b526c94 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:647b5d2512180674b14a93903de4ee0539720dc1ddc7eaadec23940aa8b3befa -size 35934 +oid sha256:c95c7e7f68133f25bca9c33246983cdd1b448ca822b1221eba54c171c8f2e051 +size 35898 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en.png index 9751c25cb4..aa8c5c7858 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:010a03fc52fb0ec9b39701d75ef26c7f23ba6195e7d3e0c4e2b7e03518425b57 -size 24287 +oid sha256:b4b6b193a6c9bd9188cde36056a512ebe10f9b24d07a33926b51876712b453b2 +size 24468 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en.png index 0fcd59d198..429daba97a 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bec4c7d06dc2cc287d2165f588c6764e5314287119960947d7e24dbc19ce901 -size 23971 +oid sha256:018e6226239c439fc683eead0acb733d1e06eebad54b1a6c2b43d8cbd2e822cc +size 24324 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en.png index ce263c7d02..0fc27b0af4 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb22d79ff43ce9a1931537880829cf5d2c13aafe3c06136c80703f2599d676cf -size 35000 +oid sha256:ecc5f3820395a67eee396556344663844fb97dc843cda96b2545c954d133795f +size 35118 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en.png index 2c4de61a77..fb5e9a7448 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3c16c9a89228f83eaaa7f9bdd7c0e36a333efb541738bd38411f9f853e20103 -size 23797 +oid sha256:dc0360e2705f3e029c1f8cabf91787a98f7d7b67e6d1f003c54ca4ffd52d08be +size 23961 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en.png index 4194458de9..2a85b2cf7f 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e2de9b362092dbd08deadac70591d4682304c69981fe27cbbeae4f26233d4aa -size 23444 +oid sha256:7ee6f9fbf05261efe52c247da3869c68c6e87f5132c52da72e56cf833ea6864d +size 23833 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en.png index a1b5d1bb59..afd2b2cbe5 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919595acfee379e3aaee66a3abba61515e8bee35a9fe0250a8a227f772109864 -size 49693 +oid sha256:94014619417d8c530dc80316c9aca36dc08997f516890d7199b24afa46e71c86 +size 50004 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en.png index 665a36ad0a..f86302a8ee 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:079ef0792efb31fbd773beb77a95bfa713213f8f5428ea1c12060094deac5442 -size 48620 +oid sha256:0a3d3c574589f06729b58775ca37bab9711c5566adff366d4a0196eaa7e10ab7 +size 47410 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en.png index 32d07be159..3ee9db165b 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17cd489adfd7a417a6972f8e36a61ecb2bcaf2a045d33e4d2236004271d4d9ff -size 48317 +oid sha256:277665230a31e1a47489a5124efa6dcaf51a5a442f0e6d4dfb510ff4896c9bd0 +size 48738 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en.png index 9ad4c83991..ba4e6885b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:215e1dbc213d493ee6e24712aa93c9a103f1148a3ea2858ff185dfdc7084ea3b -size 46546 +oid sha256:2044881fe3e54179333fc0b125972b2b410a4d3657faa10769b964e8ae0d2643 +size 45278 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png index d996699f1a..da7923cf50 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31e32566506ef9c6d34b144663b308a53f893b810960c50e394271ed5dc02fc0 -size 28564 +oid sha256:b1c09cbb0217a1fb844828590e7827c27a2e9a99ecf5aed225f4d1bd0e413c8d +size 29343 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png index 9df382c0db..68b16e509f 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6e129a426f95432f31ecb5776ae24e5fed7a3025217c70831dcab30ac270fc3 -size 33329 +oid sha256:6ee66772b20b9c1cacf1f9387c173f8170d0c132d6caddfbd389d254b2ae7aa5 +size 33977 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_1_en.png index 68d54d002a..56510ac1e2 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2eb71eb5d1cac83a03abbdc6ac670cb96993da8825ab224b43e593ae4b6ee8f -size 53719 +oid sha256:3eed90dbcc789f3db882d61a31924668a42cf78e00cd634192a5bfb831f67190 +size 53659 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_2_en.png index f71897a57b..3bc29fbe6e 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71e7b0aba9acb702371ed0eacd6d86735d47d02dfadc67e55079a454c9c8733f -size 30242 +oid sha256:228a9ed83c86a160fe494254ec654fc0bf1d6923e78f5de2b4057d386e25b333 +size 30750 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_3_en.png index 68d54d002a..56510ac1e2 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2eb71eb5d1cac83a03abbdc6ac670cb96993da8825ab224b43e593ae4b6ee8f -size 53719 +oid sha256:3eed90dbcc789f3db882d61a31924668a42cf78e00cd634192a5bfb831f67190 +size 53659 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_7_en.png index c34ec228cd..bdad7df7bc 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1c6a823d353aa2f63633bce1d0336239196f26136b532718fdf2c49c183fbe9 -size 38014 +oid sha256:64c4173147686886d5021cdaf6480644a5a520f81392ca84aa7ab5d705211bc7 +size 38682 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_8_en.png index 651a16265f..f52aac7f1c 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:359b8ffc79afd24ba68c2e7c13ba551e9d63c72b787e530667cd5de5c16782f6 -size 48002 +oid sha256:c6c73dd018eb82fe9e54ffbe38dcc491938da69791f4ff32081b2dac7549a4b2 +size 47958 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_9_en.png index 651a16265f..f52aac7f1c 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:359b8ffc79afd24ba68c2e7c13ba551e9d63c72b787e530667cd5de5c16782f6 -size 48002 +oid sha256:c6c73dd018eb82fe9e54ffbe38dcc491938da69791f4ff32081b2dac7549a4b2 +size 47958 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png index 12fa559ca5..4d2c26d91d 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84d3be38de70c774b36592543eb5636029b08f9a847566dea4f90979ec9be97c -size 27529 +oid sha256:0b0e1f00b98e463a97c9334da018ba944414526f771aa4ceaea18ab192a36863 +size 28080 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png index 543aa41952..a650636f24 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93f49929a2e696bfa8a8938107d3263dcda514f34c8b5eeb6315267126d138d0 -size 32095 +oid sha256:87e337a40741a5c7bdcb2015b8f82282ae69e1eff98e30641a02314660ecbd73 +size 32583 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_1_en.png index 53bf92ea9c..62596baf31 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4701f103d24353378826f66e6d602b0bdf43904ef6b28ee03d25af4a1062411 -size 51650 +oid sha256:66a0dc249d2df7e52a6a5f7cb3f1a94be011164eaf23d365f548b99f093d3d14 +size 51681 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_2_en.png index 68647ba764..f97ba2a853 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2223132d7446121d8bd37b3561fc24ce056aeffc8828b197a2be8c92ce64cd27 -size 29197 +oid sha256:aba88e94521e05328079fb2ac0b06fd97987f81694b3378f02c19a20bfae2572 +size 29636 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_3_en.png index 53bf92ea9c..62596baf31 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4701f103d24353378826f66e6d602b0bdf43904ef6b28ee03d25af4a1062411 -size 51650 +oid sha256:66a0dc249d2df7e52a6a5f7cb3f1a94be011164eaf23d365f548b99f093d3d14 +size 51681 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_7_en.png index d233b2890d..17526d949c 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7aa9c94a5f9c324a3229a5db951411fba6759aee18987d61f6ffc6d74ac79a60 -size 36665 +oid sha256:fd6d4b2d03d0e86a22c48f89f05c83b0e7622f4b6fc2a946ace1ef33e66b2e9a +size 37183 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_8_en.png index 084de2c14a..bf33b63f8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a952926d397cab2a689e3f4096de103bbb3075f27dbe45fff311a153ab1c9ace -size 46181 +oid sha256:d8f9a50b522d34a29ec48fcef545a0880ff0f43d464f1874c3486adb9c79e202 +size 46251 diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_9_en.png index 084de2c14a..bf33b63f8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.logout.impl_LogoutView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a952926d397cab2a689e3f4096de103bbb3075f27dbe45fff311a153ab1c9ace -size 46181 +oid sha256:d8f9a50b522d34a29ec48fcef545a0880ff0f43d464f1874c3486adb9c79e202 +size 46251 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en.png index de42fdfe94..fd32767a41 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15f9aa77f9f8fe09a10e2e22b194f2ba505d56c9097db0872b317840c0fa84c4 -size 28661 +oid sha256:218a8bafb58855056ab07b05d45c87df482483c02a5144c0117c1159135e924a +size 28637 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en.png index 6ded082e3c..a3c91c139d 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f1762442b6dcd37d5710437239055e7d40d2b741cacd1045a3eacd327fc12ee -size 28053 +oid sha256:76f1e9db9260cb23bd80bce5f45bdf323a8d24cb87757d7a32f9c6e6b632913c +size 27993 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en.png index 6ded082e3c..a3c91c139d 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f1762442b6dcd37d5710437239055e7d40d2b741cacd1045a3eacd327fc12ee -size 28053 +oid sha256:76f1e9db9260cb23bd80bce5f45bdf323a8d24cb87757d7a32f9c6e6b632913c +size 27993 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en.png index 88df849ef5..6c8ad3018b 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea5df9ecebf686ac65c265d1b74e3a0a5169d1e308eaaf4f9c205a754d8bddcc -size 40150 +oid sha256:4fda40268a68acf664e90a48391658b8dedb3d7f5d5e6b76d680ab25180d5a55 +size 40121 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en.png index 58a01b25a3..4317869283 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06170ddb862b337c00d50a531caa1f673952cc0194cb38410d3aa7cc61d95c16 -size 27653 +oid sha256:6c8f9e633578f46a6b8126ea6dcbdb5c113dd0e1107fbc4d5b2206c2f5acfb1c +size 27835 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en.png index f346d344e0..eed9b50326 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc1a0fd307ba6ea9a9f578527bae5a074e5ad67035201d8aa7d85877ee697af5 -size 26690 +oid sha256:d55afb5f76aac3d1a8ff0f1c223502fc798dc0684f9d84df79256c41ca5ea1cc +size 26706 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en.png index f346d344e0..eed9b50326 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc1a0fd307ba6ea9a9f578527bae5a074e5ad67035201d8aa7d85877ee697af5 -size 26690 +oid sha256:d55afb5f76aac3d1a8ff0f1c223502fc798dc0684f9d84df79256c41ca5ea1cc +size 26706 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en.png index f38139bcf2..0b3c962498 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:763ddaf10dfed74dbef17deccfa7037d8b643e9b8654409ce96e8192ab5ee38c -size 38418 +oid sha256:9410147c7a6a496fbd356b0dbc3b14998f20ddea6a2eeabb79b9600485285a80 +size 38628 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 21b17d8656..e1573dacb9 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:562e8457321ec1afe9ca9a2111ee19815aa25401cd925c0bcc880a25db99e0c4 -size 42100 +oid sha256:01d03620cae8382ee41703d7553913fbd1e463fcd55fd661e9b99087e14bf6b1 +size 42162 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 c26e5c44c1..bea6d32d38 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:4498edc796206821f346a2dfceb966f59f4b7adf513ae184c84d20b323ed0d67 -size 39800 +oid sha256:54d9e8714cbbc3785ccd25d6bff9c791d57acea007d6cb8774a4a5d61646b54f +size 39857 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en.png index e798d07cac..dd9010b013 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f3d6bcf6b603ea2e2a619f569960438337fe9e2ae9eea2657587cd211bbf278 -size 35444 +oid sha256:9f6ce15f59eba49738b9df2117764e760f2ddc06bd52f8386f6d78af307a4104 +size 34630 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 fa0b46361e..3264a61179 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:b9c17b8ca9966e4239e5c5fb9d9e5f0f061bd8d7fbf3bd9c76ce6473af537d05 -size 40962 +oid sha256:9a0a377b34502fb55b3ee72468533914de3c68df62531a6f40a41984c94d0047 +size 40996 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 0072d95bed..6d6a189589 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:6e73375d44fcd158c7cc2cd0f97b49c8a0f2d523772cab58778ed7b31a8a0d5e -size 38602 +oid sha256:433de908676d053d3728b8cfe03636d082691059dbe4c15b950483cfdc3e1ce8 +size 38634 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en.png index b8629728ea..0d1e17b957 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cb10d8ef6b7c6fd25e772428e03755f1c731197660ed7123b79c56629f74583 -size 33086 +oid sha256:56030e621f122f288b00dc642cf5ab4a3a09100cdcb92e068d8fc0f47f9a5328 +size 32421 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 f3d5c2ec82..5c0a10cffc 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:cef767ba47c98c5b693cfadef2ea0a260a35d3144c2dc26a3bb52bf632700d9c -size 44141 +oid sha256:61b6259baf820caca8eb8a28052142c56ee88a952148e204c25a53f709b87fe4 +size 44542 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 5c91aae383..599ce75592 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:2ef46c819c931938a4fd856bb2fc697c964d4445d6cf9f8b5e2f341f22805061 -size 42001 +oid sha256:2f246482f43b0298e8948925fda23d9c306f56b484d42eeedbca1cce2dfb6bb2 +size 42382 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en.png index 214e107f12..224e1705c4 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de188fac87a59e6765360282655c081d7b93e7dfa47837b0c56dd77a9892ed03 -size 34616 +oid sha256:2ab51540f79f485e98b311210fc7d8b98ccb174b9e689587559878c245877f4a +size 38492 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 7f69976784..654f566376 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:6b53f00607190915bfb683c0bef1bd11f0804651fe6bf57bc9312afa20768a75 -size 42924 +oid sha256:b9962bf5c4f9870d51cd3eb3346bf0e2054c5f7e21111f57c7a7bb1938e81aab +size 43211 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 20dcda7f44..564035b900 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:6cd74bb51213ae932addc3dbaff2bd20d877a249c46ac5bb7656e03566189b38 -size 40748 +oid sha256:fbf5c303797332b7419e1529ddaa8d1f2f1cd6ed2086654880c7702fc3d62070 +size 40971 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en.png index 82f91a65d5..3d21f69991 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcad0014629c828846e05247a1ca0767c45906bc15378a00cb9d4e6cd7280dc1 -size 32380 +oid sha256:47fc45150924e2c1aacc86889824f7c0d7382af94ef420ff690871d75c7c3e23 +size 36212 From d215354e6482c9c0fce12295bf9672a0948fef7a Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 29 Apr 2026 12:54:03 +0200 Subject: [PATCH 025/471] Remove legacy `mx-reply` from `toPlainText` formatted event contents (#6683) --- .../matrix/ui/messages/ToHtmlDocument.kt | 40 +++++++++++++++++-- .../matrix/ui/messages/ToPlainText.kt | 5 +++ .../matrix/ui/messages/ToPlainTextTest.kt | 15 +++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt index ee9de51681..d29a28cba9 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToHtmlDocument.kt @@ -12,8 +12,10 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat -import io.element.android.wysiwyg.utils.HtmlToDomParser +import org.jsoup.Jsoup import org.jsoup.nodes.Document +import org.jsoup.nodes.Document.OutputSettings +import org.jsoup.safety.Safelist /** * Converts the HTML string [FormattedBody.body] to a [Document] by parsing it. @@ -34,9 +36,9 @@ fun FormattedBody.toHtmlDocument( ?.trimEnd() ?.let { formattedBody -> val dom = if (prefix != null) { - HtmlToDomParser.document("$prefix $formattedBody") + CustomHtmlToDomParser.document("$prefix $formattedBody") } else { - HtmlToDomParser.document(formattedBody) + CustomHtmlToDomParser.document(formattedBody) } // Prepend `@` to mentions @@ -60,3 +62,35 @@ private fun fixMentions( } } } + +/** Custom Html to DOM parser, based on the one included in the rich text editor library. */ +private object CustomHtmlToDomParser { + fun document(html: String): Document { + val outputSettings = OutputSettings().prettyPrint(false).indentAmount(0) + val cleanHtml = Jsoup.clean(html, "", safeList, outputSettings) + return Jsoup.parse(cleanHtml) + } + + private val safeList = Safelist() + .addTags( + "a", + "b", + "strong", + "i", + "em", + "u", + "del", + "code", + "ul", + "ol", + "li", + "pre", + "blockquote", + "p", + "br", + // Add custom `mx-reply` tag, even if it's just to remove its contents from the plain text version of the message + "mx-reply" + ) + .addAttributes("a", "href", "data-mention-type", "contenteditable") + .addAttributes("ol", "start") +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt index 34dc72aa1a..cf8b03b80d 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainText.kt @@ -65,6 +65,8 @@ fun Document.toPlainText(): String { return visitor.build() } +private const val FALLBACK_REPLY_NODE_TAG = "mx-reply" + private class PlainTextNodeVisitor : NodeVisitor { private val builder = StringBuilder() @@ -92,6 +94,9 @@ private class PlainTextNodeVisitor : NodeVisitor { } else { builder.append("• ") } + } else if (node is Element && node.tagName() == FALLBACK_REPLY_NODE_TAG) { + // Remove the fallback reply node and its contents so they aren't added to the plain text message + node.remove() } else if (node is Element && node.isBlock && builder.lastOrNull() != '\n') { builder.append("\n") } diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainTextTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainTextTest.kt index 607f825401..ce0ef4cf31 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainTextTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/messages/ToPlainTextTest.kt @@ -136,4 +136,19 @@ class ToPlainTextTest { ) assertThat(messageType.toPlainText(permalinkParser = FakePermalinkParser())).isEqualTo("This is the fallback text") } + + @Test + fun `TextMessageType toPlainText - ignores mx-reply element`() { + val messageType = TextMessageType( + body = "This is the fallback text", + formatted = FormattedBody( + format = MessageFormat.HTML, + body = """ + In reply to... + This is the message content. + """.trimIndent() + ) + ) + assertThat(messageType.toPlainText(permalinkParser = FakePermalinkParser())).isEqualTo("This is the message content.") + } } From 3b21d698eefee5a427b8f0cd6f6352ed923b2b99 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 29 Apr 2026 12:05:16 +0100 Subject: [PATCH 026/471] Fix tests Co-Authored-By: Benoit Marty <3940906+bmarty@users.noreply.github.com> --- .../FakeFfiGrantLoginWithQrCodeHandler.kt | 4 +- .../RustLinkDesktopHandlerTest.kt | 39 ++++++++++++++++++- .../RustLinkMobileHandlerTest.kt | 39 ++++++++++++++++++- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiGrantLoginWithQrCodeHandler.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiGrantLoginWithQrCodeHandler.kt index cd0733695b..0899b16325 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiGrantLoginWithQrCodeHandler.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiGrantLoginWithQrCodeHandler.kt @@ -16,8 +16,8 @@ import org.matrix.rustcomponents.sdk.NoHandle import org.matrix.rustcomponents.sdk.QrCodeData class FakeFfiGrantLoginWithQrCodeHandler( - private val generateResult: () -> Unit = {}, - private val scanResult: (QrCodeData) -> Unit = {}, + private val generateResult: suspend () -> Unit = {}, + private val scanResult: suspend (QrCodeData) -> Unit = {}, ) : GrantLoginWithQrCodeHandler(NoHandle) { private var generateProgressListener: GrantGeneratedQrLoginProgressListener? = null private var scanProgressListener: GrantQrLoginProgressListener? = null diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt index a180e4d515..fd635e2da6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkDesktopHandlerTest.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.linknewdevice.ErrorType import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopStep import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler import io.element.android.libraries.matrix.test.QR_CODE_DATA +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher @@ -29,7 +30,13 @@ import org.matrix.rustcomponents.sdk.QrCodeDecodeException class RustLinkDesktopHandlerTest { @Test fun `handleScannedQrCode function works as expected`() = runTest { - val handler = FakeFfiGrantLoginWithQrCodeHandler() + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + scanResult = { + // Ensure that the coroutine is hold + completable.await() + } + ) val sut = createRustLinkDesktopHandler( handler, ) @@ -53,6 +60,36 @@ class RustLinkDesktopHandlerTest { handler.emitScanProgress(progress) assertThat(awaitItem()).isEqualTo(expectedStep) } + // scan returns, no new event is emitted + completable.complete(Unit) + expectNoEvents() + } + } + + @Test + fun `when scan does not emits the Done state, the code emits it`() = runTest { + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + scanResult = { + // Ensure that the coroutine is hold + completable.await() + } + ) + val sut = createRustLinkDesktopHandler( + handler, + ) + sut.linkDesktopStep.test { + val initialItem = awaitItem() + assertThat(initialItem).isEqualTo(LinkDesktopStep.Uninitialized) + backgroundScope.launch { + sut.handleScannedQrCode(QR_CODE_DATA) + } + runCurrent() + handler.emitScanProgress(GrantQrLoginProgress.Starting) + assertThat(awaitItem()).isEqualTo(LinkDesktopStep.Starting) + // scan returns, Done event is emitted + completable.complete(Unit) + assertThat(awaitItem()).isEqualTo(LinkDesktopStep.Done) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt index aa13996e8a..9f71cb7169 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiCheckCodeS import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiQrCodeData import io.element.android.libraries.matrix.test.QR_CODE_DATA_RECIPROCATE +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher @@ -30,7 +31,13 @@ import org.matrix.rustcomponents.sdk.HumanQrGrantLoginException class RustLinkMobileHandlerTest { @Test fun `start function works as expected`() = runTest { - val handler = FakeFfiGrantLoginWithQrCodeHandler() + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + generateResult = { + // Ensure that the coroutine is hold + completable.await() + } + ) val sut = createRustLinkMobileHandler( handler, ) @@ -56,6 +63,36 @@ class RustLinkMobileHandlerTest { handler.emitGenerateProgress(progress) assertThat(awaitItem()).isInstanceOf(expectedStepClass) } + // generate returns, no new event is emitted + completable.complete(Unit) + expectNoEvents() + } + } + + @Test + fun `when generates does not emits the Done state, the code emits it`() = runTest { + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + generateResult = { + // Ensure that the coroutine is hold + completable.await() + } + ) + val sut = createRustLinkMobileHandler( + handler, + ) + sut.linkMobileStep.test { + val initialItem = awaitItem() + assertThat(initialItem).isEqualTo(LinkMobileStep.Uninitialized) + backgroundScope.launch { + sut.start() + } + runCurrent() + handler.emitGenerateProgress(GrantGeneratedQrLoginProgress.Starting) + assertThat(awaitItem()).isEqualTo(LinkMobileStep.Starting) + // generate returns, Done event is emitted + completable.complete(Unit) + assertThat(awaitItem()).isEqualTo(LinkMobileStep.Done) } } From f0dc4eeace0c8f19304fce4f701afa749809a9cd Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 24 Apr 2026 11:37:00 +0200 Subject: [PATCH 027/471] feat: Update call started timeline item + declined support --- .../components/TimelineItemCallNotifyView.kt | 94 ++++++++++++------- .../timeline/components/TimelineItemRow.kt | 1 + .../event/TimelineItemContentFactory.kt | 8 +- .../TimelineItemRtcNotificationContent.kt | 21 ++++- .../DefaultMessageSummaryFormatter.kt | 8 +- .../impl/DefaultRoomLatestEventFormatter.kt | 3 +- .../impl/RtcNotificationContentFormatter.kt | 39 ++++++++ .../api/timeline/item/event/EventContent.kt | 3 +- .../item/event/TimelineEventContentMapper.kt | 3 +- 9 files changed, 138 insertions(+), 42 deletions(-) create mode 100644 libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatter.kt 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 dfd1b2ed0e..aa004a6259 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 @@ -22,17 +22,19 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent -import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenuAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -42,6 +44,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable internal fun TimelineItemCallNotifyView( + timelineRoomInfo: TimelineRoomInfo, event: TimelineItem.Event, content: TimelineItemRtcNotificationContent, onLongClick: (TimelineItem.Event) -> Unit, @@ -62,37 +65,22 @@ internal fun TimelineItemCallNotifyView( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, ) { - Avatar( - avatarData = event.senderAvatar, - avatarType = AvatarType.User, + Icon( + modifier = Modifier.size(20.sp.toDp()), + imageVector = getIcon(timelineRoomInfo, content), + contentDescription = null, + tint = ElementTheme.colors.iconSecondary, ) - Column(modifier = Modifier.weight(1f)) { - Text( - text = event.safeSenderName, - style = ElementTheme.typography.fontBodyLgMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - Row( - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - modifier = Modifier.size(20.sp.toDp()), - imageVector = - if (content.callIntent == CallIntent.AUDIO) CompoundIcons.VoiceCallSolid() else CompoundIcons.VideoCallSolid(), - contentDescription = null, - tint = ElementTheme.colors.iconSecondary, - ) - Text( - text = stringResource(CommonStrings.common_call_started), - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } - } + + Text( + modifier = Modifier.weight(1f), + text = stringResource(getTextRes(timelineRoomInfo, content)), + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Text( text = event.sentTime, style = ElementTheme.typography.fontBodyMdRegular, @@ -103,15 +91,51 @@ internal fun TimelineItemCallNotifyView( } } +private fun getTextRes( + timelineRoomInfo: TimelineRoomInfo, + content: TimelineItemRtcNotificationContent +): Int = if (timelineRoomInfo.isDm) { + when (content.state) { + RtcNotificationState.Declined -> CommonStrings.common_call_declined + RtcNotificationState.DeclinedByMe -> CommonStrings.common_call_you_declined + RtcNotificationState.None -> CommonStrings.common_call_started + } +} else { + // Only show declined info in DMs + CommonStrings.common_call_started +} + +@Composable +private fun getIcon( + timelineRoomInfo: TimelineRoomInfo, + content: TimelineItemRtcNotificationContent +): ImageVector { + val showAsDeclined = timelineRoomInfo.isDm && ( + content.state == RtcNotificationState.Declined || + content.state == RtcNotificationState.DeclinedByMe + ) + val icon = if (showAsDeclined) { + if (content.callIntent == CallIntent.AUDIO) CompoundIcons.VoiceCallDeclinedSolid() else CompoundIcons.VideoCallDeclinedSolid() + } else { + if (content.callIntent == CallIntent.AUDIO) CompoundIcons.VoiceCallSolid() else CompoundIcons.VideoCallSolid() + } + return icon +} + @PreviewsDayNight @Composable internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { listOf( - TimelineItemRtcNotificationContent(CallIntent.AUDIO), - TimelineItemRtcNotificationContent(CallIntent.VIDEO), - ).forEach { content -> + (aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.None)), + (aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None)), + (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined)), + (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined)), + (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.DeclinedByMe)), + (aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None)), + ).forEach { (info, content) -> TimelineItemCallNotifyView( + timelineRoomInfo = info, event = aTimelineItemEvent(content = content), content = content, onLongClick = {}, 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 e75df2f89f..7cd99651a4 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 @@ -125,6 +125,7 @@ internal fun TimelineItemRow( is TimelineItemRtcNotificationContent -> { TimelineItemCallNotifyView( modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp), + timelineRoomInfo = timelineRoomInfo, event = timelineItem, content = timelineItem.content, onLongClick = onLongClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index dff195e833..64320622ba 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.timeline.factories.event import dev.zacsweers.metro.Inject import io.element.android.features.location.api.Location +import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent @@ -104,7 +105,12 @@ class TimelineItemContentFactory( is PollContent -> pollFactory.create(eventId, isEditable, isOutgoing, itemContent) is UnableToDecryptContent -> utdFactory.create(itemContent) is CallNotifyContent -> TimelineItemRtcNotificationContent( - itemContent.callIntent + callIntent = itemContent.callIntent, + state = when { + itemContent.declinedBy.isEmpty().not() && itemContent.declinedBy.any { it == sessionId } -> RtcNotificationState.DeclinedByMe + itemContent.declinedBy.isEmpty().not() -> RtcNotificationState.Declined + else -> RtcNotificationState.None + } ) is UnknownContent -> TimelineItemUnknownContent is LiveLocationContent -> { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt index 53facfc675..df1c9f5693 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt @@ -9,7 +9,24 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.notification.CallIntent +import io.element.android.libraries.matrix.api.timeline.item.event.EventType -class TimelineItemRtcNotificationContent(val callIntent: CallIntent) : TimelineItemEventContent { - override val type: String = "org.matrix.msc4075.rtc.notification" +// For now this is just an enum, but could be a +// sealed class if we need the list of users who declined. +enum class RtcNotificationState { + /** Some users have declined */ + Declined, + + /** I have declined this call */ + DeclinedByMe, + + // Future sates could be `Missed`? `ongoing`... + None +} + +class TimelineItemRtcNotificationContent( + val callIntent: CallIntent, + val state: RtcNotificationState, +) : TimelineItemEventContent { + override val type: String = EventType.RTC_NOTIFICATION } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt index c48f2dae40..5437711bb7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt @@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.utils.messagesummary import android.content.Context import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent @@ -56,7 +57,12 @@ class DefaultMessageSummaryFormatter( is TimelineItemFileContent -> context.getString(CommonStrings.common_file) is TimelineItemAudioContent -> context.getString(CommonStrings.common_audio) is TimelineItemLegacyCallInviteContent -> context.getString(CommonStrings.common_unsupported_call) - is TimelineItemRtcNotificationContent -> context.getString(CommonStrings.common_call_started) + is TimelineItemRtcNotificationContent -> when (content.state) { + RtcNotificationState.Declined -> + context.getString(CommonStrings.common_call_declined) + RtcNotificationState.DeclinedByMe -> context.getString(CommonStrings.common_call_you_declined) + RtcNotificationState.None -> context.getString(CommonStrings.common_call_started) + } } // Truncate the message to a safe length to avoid crashes in Compose .toSafeLength() diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index b9b6467c92..eac0e9e88d 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -55,6 +55,7 @@ class DefaultRoomLatestEventFormatter( private val profileChangeContentFormatter: ProfileChangeContentFormatter, private val stateContentFormatter: StateContentFormatter, private val permalinkParser: PermalinkParser, + private val rtcNotificationContentFormatter: RtcNotificationContentFormatter, ) : RoomLatestEventFormatter { override fun format( latestEvent: LatestEventValue.Local, @@ -121,7 +122,7 @@ class DefaultRoomLatestEventFormatter( message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is LegacyCallInviteContent -> sp.getString(CommonStrings.common_unsupported_call) - is CallNotifyContent -> sp.getString(CommonStrings.common_call_started) + is CallNotifyContent -> rtcNotificationContentFormatter.format(content, isDmRoom) }?.take(DEFAULT_SAFE_LENGTH) } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatter.kt new file mode 100644 index 0000000000..ab3fb9433d --- /dev/null +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatter.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.eventformatter.impl + +import dev.zacsweers.metro.Inject +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.services.toolbox.api.strings.StringProvider + +@Inject +class RtcNotificationContentFormatter( + private val matrixClient: MatrixClient, + private val sp: StringProvider, +) { + fun format( + content: CallNotifyContent, + isDm: Boolean, + ): CharSequence { + return if (isDm) { + val isDeclined = content.declinedBy.isNotEmpty() + val isDeclinedByMe = content.declinedBy.any { matrixClient.isMe(it) } + if (isDeclinedByMe) { + sp.getString(CommonStrings.common_call_you_declined) + } else if (isDeclined) { + sp.getString(CommonStrings.common_call_declined) + } else { + sp.getString(CommonStrings.common_call_started) + } + } else { + sp.getString(CommonStrings.common_call_started) + } + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index d91d404a0b..f323b316f7 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -119,7 +119,8 @@ data class LiveLocationContent( data object LegacyCallInviteContent : EventContent data class CallNotifyContent( - val callIntent: CallIntent + val callIntent: CallIntent, + val declinedBy: List ) : EventContent data object UnknownContent : EventContent diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index edfe9a3543..fa671bc546 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -153,7 +153,8 @@ class TimelineEventContentMapper( CallIntent.AUDIO } else { CallIntent.VIDEO - } + }, + declinedBy = it.declinedBy.map(::UserId) ) } } From a6622c678714bec5bc8a749e8e65c1c8dbd51fbb Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 29 Apr 2026 16:03:19 +0200 Subject: [PATCH 028/471] fix deteckt | unneeded paranthesis --- .../components/TimelineItemCallNotifyView.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemCallNotifyView.kt index aa004a6259..c11aab08ff 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 @@ -127,12 +127,12 @@ private fun getIcon( internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { listOf( - (aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.None)), - (aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None)), - (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined)), - (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined)), - (aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.DeclinedByMe)), - (aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None)), + aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.None), + aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.DeclinedByMe), + aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None), ).forEach { (info, content) -> TimelineItemCallNotifyView( timelineRoomInfo = info, From 61548167967e18b4e217af6d75dff5bee13cf39b Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 29 Apr 2026 16:21:36 +0200 Subject: [PATCH 029/471] cleanup of the RTCNotificationState enum --- .../components/TimelineItemCallNotifyView.kt | 24 +++++++++---------- .../event/TimelineItemContentFactory.kt | 8 +++---- .../TimelineItemRtcNotificationContent.kt | 15 ++++-------- .../DefaultMessageSummaryFormatter.kt | 12 ++++++---- 4 files changed, 28 insertions(+), 31 deletions(-) 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 c11aab08ff..f4d0d97495 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 @@ -96,9 +96,10 @@ private fun getTextRes( content: TimelineItemRtcNotificationContent ): Int = if (timelineRoomInfo.isDm) { when (content.state) { - RtcNotificationState.Declined -> CommonStrings.common_call_declined - RtcNotificationState.DeclinedByMe -> CommonStrings.common_call_you_declined - RtcNotificationState.None -> CommonStrings.common_call_started + is RtcNotificationState.Declined -> { + if (content.state.byMe) CommonStrings.common_call_you_declined else CommonStrings.common_call_declined + } + RtcNotificationState.Started -> CommonStrings.common_call_started } } else { // Only show declined info in DMs @@ -110,10 +111,7 @@ private fun getIcon( timelineRoomInfo: TimelineRoomInfo, content: TimelineItemRtcNotificationContent ): ImageVector { - val showAsDeclined = timelineRoomInfo.isDm && ( - content.state == RtcNotificationState.Declined || - content.state == RtcNotificationState.DeclinedByMe - ) + val showAsDeclined = timelineRoomInfo.isDm && content.state is RtcNotificationState.Declined val icon = if (showAsDeclined) { if (content.callIntent == CallIntent.AUDIO) CompoundIcons.VoiceCallDeclinedSolid() else CompoundIcons.VideoCallDeclinedSolid() } else { @@ -127,12 +125,12 @@ private fun getIcon( internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { listOf( - aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.None), - aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.DeclinedByMe), - aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.None), + aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Started), + aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Started), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined(false)), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined(false)), + aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined(true)), + aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Started), ).forEach { (info, content) -> TimelineItemCallNotifyView( timelineRoomInfo = info, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index 64320622ba..2d884d75fd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -106,10 +106,10 @@ class TimelineItemContentFactory( is UnableToDecryptContent -> utdFactory.create(itemContent) is CallNotifyContent -> TimelineItemRtcNotificationContent( callIntent = itemContent.callIntent, - state = when { - itemContent.declinedBy.isEmpty().not() && itemContent.declinedBy.any { it == sessionId } -> RtcNotificationState.DeclinedByMe - itemContent.declinedBy.isEmpty().not() -> RtcNotificationState.Declined - else -> RtcNotificationState.None + state = if (itemContent.declinedBy.isNotEmpty()) { + RtcNotificationState.Declined(itemContent.declinedBy.any { it == sessionId }) + } else { + RtcNotificationState.Started } ) is UnknownContent -> TimelineItemUnknownContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt index df1c9f5693..c09ccd1d21 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt @@ -11,17 +11,12 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.notification.CallIntent import io.element.android.libraries.matrix.api.timeline.item.event.EventType -// For now this is just an enum, but could be a -// sealed class if we need the list of users who declined. -enum class RtcNotificationState { - /** Some users have declined */ - Declined, +// State of the call, for now only isDeclined but in the future could be missed, active. +sealed class RtcNotificationState { + /** Some users have declined, byMe indicates if the current user is one of them. */ + data class Declined(val byMe: Boolean) : RtcNotificationState() - /** I have declined this call */ - DeclinedByMe, - - // Future sates could be `Missed`? `ongoing`... - None + object Started : RtcNotificationState() } class TimelineItemRtcNotificationContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt index 5437711bb7..210e123595 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/DefaultMessageSummaryFormatter.kt @@ -58,10 +58,14 @@ class DefaultMessageSummaryFormatter( is TimelineItemAudioContent -> context.getString(CommonStrings.common_audio) is TimelineItemLegacyCallInviteContent -> context.getString(CommonStrings.common_unsupported_call) is TimelineItemRtcNotificationContent -> when (content.state) { - RtcNotificationState.Declined -> - context.getString(CommonStrings.common_call_declined) - RtcNotificationState.DeclinedByMe -> context.getString(CommonStrings.common_call_you_declined) - RtcNotificationState.None -> context.getString(CommonStrings.common_call_started) + is RtcNotificationState.Declined -> { + if (content.state.byMe) { + context.getString(CommonStrings.common_call_you_declined) + } else { + context.getString(CommonStrings.common_call_declined) + } + } + RtcNotificationState.Started -> context.getString(CommonStrings.common_call_started) } } // Truncate the message to a safe length to avoid crashes in Compose From 531d9b3d47d61b1ba82230a477f654007979332d Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 29 Apr 2026 16:29:13 +0200 Subject: [PATCH 030/471] fix: consist, use sealed interface instead of class --- .../model/event/TimelineItemRtcNotificationContent.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt index c09ccd1d21..2359f196a9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRtcNotificationContent.kt @@ -12,11 +12,11 @@ import io.element.android.libraries.matrix.api.notification.CallIntent import io.element.android.libraries.matrix.api.timeline.item.event.EventType // State of the call, for now only isDeclined but in the future could be missed, active. -sealed class RtcNotificationState { +sealed interface RtcNotificationState { /** Some users have declined, byMe indicates if the current user is one of them. */ - data class Declined(val byMe: Boolean) : RtcNotificationState() + data class Declined(val byMe: Boolean) : RtcNotificationState - object Started : RtcNotificationState() + object Started : RtcNotificationState } class TimelineItemRtcNotificationContent( From 66f68fdccb292d95425a0e1227dc61f06d836a23 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 29 Apr 2026 17:27:51 +0200 Subject: [PATCH 031/471] fixup test compilation --- .../messages/impl/actionlist/ActionListPresenterTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index 8c7f290441..20b636081a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -17,6 +17,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.features.messages.impl.fixtures.aMessageEvent import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo +import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent @@ -1169,7 +1170,7 @@ class ActionListPresenterTest { val initialState = awaitItem() val messageEvent = aMessageEvent( isMine = true, - content = TimelineItemRtcNotificationContent(callIntent = CallIntent.VIDEO), + content = TimelineItemRtcNotificationContent(callIntent = CallIntent.VIDEO, state = RtcNotificationState.Started), ) initialState.eventSink.invoke( ActionListEvent.ComputeForMessage( From 6ba4679908ae1a3c7b0f698c10fda649aff9f8a5 Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Thu, 30 Apr 2026 00:08:33 +0800 Subject: [PATCH 032/471] Change native back button behavior in EC view (close settings in EC with os native back) (#6642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change native back button behavior in EC view: - inject escape into webview instead of going back. - the webview will call back when no other modal is open. * call down and up in the webview + make sure that we fall back to close pip in case the webview did not handle the esc action. --------- Co-authored-by: Jorge Martín --- .../call/impl/ui/CallScreenBackPressPolicy.kt | 26 +++ .../features/call/impl/ui/CallScreenView.kt | 36 +++-- .../call/ui/CallScreenBackPressPolicyTest.kt | 96 +++++++++++ .../features/call/ui/CallScreenViewTest.kt | 150 ++++++++++++++++++ 4 files changed, 298 insertions(+), 10 deletions(-) create mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt create mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt create mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt new file mode 100644 index 0000000000..cd47cd8bb1 --- /dev/null +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.impl.ui +internal sealed interface CallScreenBackPressAction { + data object DispatchEscapeToWebView : CallScreenBackPressAction + data object EnterPictureInPicture : CallScreenBackPressAction +} + +internal object CallScreenBackPressPolicy { + fun resolve( + supportPip: Boolean, + hasWebView: Boolean, + fromNative: Boolean, + ): CallScreenBackPressAction? { + return when { + hasWebView && fromNative -> CallScreenBackPressAction.DispatchEscapeToWebView + hasWebView && supportPip -> CallScreenBackPressAction.EnterPictureInPicture + else -> null + } + } +} 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 f8657a9ece..a54b726a78 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 @@ -64,11 +64,15 @@ internal fun CallScreenView( requestPermissions: (Array, RequestPermissionCallback) -> Unit, modifier: Modifier = Modifier, ) { - fun handleBack() { - if (pipState.supportPip) { - pipState.eventSink.invoke(PictureInPictureEvents.EnterPictureInPicture) - } else { - state.eventSink(CallScreenEvents.Hangup) + var callWebView by remember { mutableStateOf(null) } + + fun handleBack(fromNative: Boolean = false) { + when (CallScreenBackPressPolicy.resolve(supportPip = pipState.supportPip, hasWebView = callWebView != null, fromNative)) { + CallScreenBackPressAction.EnterPictureInPicture -> + pipState.eventSink(PictureInPictureEvents.EnterPictureInPicture) + CallScreenBackPressAction.DispatchEscapeToWebView -> + callWebView?.dispatchEscKeyEvent() + null -> Timber.d("Back press with unsupported pip is a no-op") } } @@ -76,7 +80,7 @@ internal fun CallScreenView( modifier = modifier, ) { padding -> BackHandler { - handleBack() + handleBack(fromNative = true) } if (state.webViewError != null) { ErrorDialog( @@ -111,6 +115,7 @@ internal fun CallScreenView( }, onConsoleMessage = onConsoleMessage, onCreateWebView = { webView -> + callWebView = webView webView.addBackHandler(onBackPressed = ::handleBack) val interceptor = WebViewWidgetMessageInterceptor( webView = webView, @@ -135,6 +140,7 @@ internal fun CallScreenView( pipState.eventSink(PictureInPictureEvents.SetPipController(pipController)) }, onDestroyWebView = { + callWebView = null // Reset audio mode webViewAudioManager?.onCallStopped() } @@ -143,6 +149,7 @@ internal fun CallScreenView( AsyncData.Uninitialized, is AsyncData.Loading -> ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait)) + is AsyncData.Failure -> { Timber.e(state.urlState.error, "WebView failed to load URL: ${state.urlState.error.message}") ErrorDialog( @@ -150,6 +157,7 @@ internal fun CallScreenView( onSubmit = { state.eventSink(CallScreenEvents.Hangup) }, ) } + is AsyncData.Success -> Unit } } @@ -248,15 +256,18 @@ private fun WebView.setup( private fun WebView.addBackHandler(onBackPressed: () -> Unit) { addJavascriptInterface( - object { - @Suppress("unused") - @JavascriptInterface - fun onBackPressed() = onBackPressed() + JavascriptBackHandler { + onBackPressed() }, "backHandler" ) } +private fun WebView.dispatchEscKeyEvent() { + dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_ESCAPE)) + dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_ESCAPE)) +} + @PreviewsDayNight @Composable internal fun CallScreenViewPreview( @@ -275,3 +286,8 @@ internal fun CallScreenViewPreview( internal fun InvalidAudioDeviceDialogPreview() = ElementPreview { InvalidAudioDeviceDialog(invalidAudioDeviceReason = InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED) {} } + +internal fun interface JavascriptBackHandler { + @JavascriptInterface + fun onBackPressed() +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt new file mode 100644 index 0000000000..f07f7039d3 --- /dev/null +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.ui + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.call.impl.ui.CallScreenBackPressAction +import io.element.android.features.call.impl.ui.CallScreenBackPressPolicy +import org.junit.Test + +class CallScreenBackPressPolicyTest { + @Test + fun `resolve returns dispatch escape when a web view is available and native button is pressed`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = true, + fromNative = true, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) + } + + @Test + fun `resolve dispatch escape when there is a web view and pip is supported on native button press`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = true, + fromNative = true, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) + } + + @Test + fun `resolve returns hangup when there is no web view and pip is not supported from native button`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = false, + fromNative = true, + ) + + assertThat(result).isNull() + } + + @Test + fun `resolve returns hangup when there is no web view even though pip is supported from native button`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = false, + fromNative = true, + ) + + assertThat(result).isNull() + } + + @Test + fun `resolve goes to pip if its not from native but from the webview`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = true, + fromNative = false, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.EnterPictureInPicture) + } + @Test + fun `resolve hangs up if its not from native but from the webview and pip is not supported`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = true, + fromNative = false, + ) + + assertThat(result).isNull() + } + + @Test + fun `invalid cases (event comes from webview but there is now webview) all result in hangup`() { + val withPipSupport = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = false, + fromNative = false, + ) + assertThat(withPipSupport).isNull() + val withOutPipSupport = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = false, + fromNative = false, + ) + assertThat(withOutPipSupport).isNull() + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt new file mode 100644 index 0000000000..35b90a6716 --- /dev/null +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.ui + +import android.view.KeyEvent +import android.webkit.WebView +import androidx.activity.ComponentActivity +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.call.impl.pip.PictureInPictureEvents +import io.element.android.features.call.impl.pip.aPictureInPictureState +import io.element.android.features.call.impl.ui.CallScreenEvents +import io.element.android.features.call.impl.ui.CallScreenView +import io.element.android.features.call.impl.ui.JavascriptBackHandler +import io.element.android.features.call.impl.ui.aCallScreenState +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.pressBackKey +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.Implementation +import org.robolectric.annotation.Implements +import org.robolectric.annotation.Resetter +import org.robolectric.shadows.ShadowWebView + +@RunWith(AndroidJUnit4::class) +class CallScreenViewTest { + @get:Rule + val rule = createAndroidComposeRule() + + @Test + fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() { + val callEvents = EventsRecorder() + + rule.setCallScreenView( + state = aCallScreenState(eventSink = callEvents), + useInspectionMode = true, + ) + + rule.pressBackKey() + + callEvents.assertEmpty() + } + + @Config(shadows = [RecordingShadowWebView::class]) + @Test + fun `pressing back key dispatches escape key events to web view when pip is unsupported`() { + rule.setCallScreenView( + state = aCallScreenState(), + useInspectionMode = false, + ) + + rule.pressBackKey() + + val dispatchedEvents = RecordingShadowWebView.dispatchedEvents + assertEquals(2, dispatchedEvents.size) + assertEquals(KeyEvent.ACTION_DOWN, dispatchedEvents[0].action) + assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[0].keyCode) + assertEquals(KeyEvent.ACTION_UP, dispatchedEvents[1].action) + assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[1].keyCode) + } + + @Config(shadows = [RecordingShadowWebView::class]) + @Test + fun `web view javascript back handler emits pip event when pip is supported`() { + val pipEvents = EventsRecorder() + + rule.setCallScreenView( + state = aCallScreenState(), + useInspectionMode = false, + pipState = aPictureInPictureState( + supportPip = true, + eventSink = pipEvents, + ), + ) + + rule.runOnIdle { + RecordingShadowWebView.invokeJavascriptBackHandler() + } + + pipEvents.assertSize(2) + pipEvents.assertTrue(0) { it is PictureInPictureEvents.SetPipController } + pipEvents.assertTrue(1) { it is PictureInPictureEvents.EnterPictureInPicture } + } +} + +private fun AndroidComposeTestRule.setCallScreenView( + state: io.element.android.features.call.impl.ui.CallScreenState, + useInspectionMode: Boolean, + pipState: io.element.android.features.call.impl.pip.PictureInPictureState = aPictureInPictureState(supportPip = false), +) { + setContent { + // Inspection mode disables AndroidView creation; keep it configurable per test. + CompositionLocalProvider(LocalInspectionMode provides useInspectionMode) { + CallScreenView( + state = state, + pipState = pipState, + onConsoleMessage = {}, + requestPermissions = { _, _ -> }, + ) + } + } +} + +@Implements(WebView::class) +internal class RecordingShadowWebView : ShadowWebView() { + companion object { + val dispatchedEvents = mutableListOf() + private var backHandlerJavascriptInterface: JavascriptBackHandler? = null + + @Resetter + @JvmStatic + @Suppress("unused") + fun resetRecordedEvents() { + dispatchedEvents.clear() + backHandlerJavascriptInterface = null + } + + fun invokeJavascriptBackHandler() { + val backHandler = checkNotNull(backHandlerJavascriptInterface) { "Expected backHandler JavaScript interface to be registered" } + backHandler.onBackPressed() + } + } + + @Implementation + protected override fun addJavascriptInterface(`object`: Any, name: String) { + super.addJavascriptInterface(`object`, name) + if (name == "backHandler") { + backHandlerJavascriptInterface = `object` as? JavascriptBackHandler + } + } + + @Implementation + @Suppress("unused") + fun dispatchKeyEvent(event: KeyEvent): Boolean { + dispatchedEvents += KeyEvent(event) + return false + } +} From 7940b8f86511d45e19ba1be4900d5ac708c6fecd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 16:11:36 +0000 Subject: [PATCH 033/471] Update dependency io.sentry:sentry-android to v8.40.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b942901e19..19b9c29468 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -222,7 +222,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics posthog = "com.posthog:posthog-android:3.39.0" -sentry = "io.sentry:sentry-android:8.39.1" +sentry = "io.sentry:sentry-android:8.40.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.33.2" From da36323006643ac0c59f87e6f699055cc2138caa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:27:57 +0000 Subject: [PATCH 034/471] Update dependency androidx.compose:compose-bom to v2026.04.01 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b942901e19..85cea1c436 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ camera = "1.6.0" work = "2.11.2" # Compose -compose_bom = "2026.03.01" +compose_bom = "2026.04.01" # Coroutines coroutines = "1.10.2" From a76b55e5808c4f8dcdcf7d6e000e14dbf029269d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 28 Apr 2026 17:55:56 +0200 Subject: [PATCH 035/471] Add a way to tweak MAS url. --- enterprise | 2 +- .../features/enterprise/api/EnterpriseService.kt | 1 + .../enterprise/api/SessionEnterpriseService.kt | 1 + .../enterprise/impl/DefaultEnterpriseService.kt | 2 +- .../enterprise/impl/DefaultSessionEnterpriseService.kt | 1 + .../features/enterprise/test/FakeEnterpriseService.kt | 5 +++++ .../enterprise/test/FakeSessionEnterpriseService.kt | 5 +++++ .../linknewdevice/impl/LinkNewDeviceFlowNode.kt | 10 ++++++++-- .../impl/DefaultLinkNewDeviceEntryPointTest.kt | 2 ++ .../preferences/impl/root/PreferencesRootPresenter.kt | 8 +++++++- .../impl/root/PreferencesRootPresenterTest.kt | 9 ++++++++- features/securebackup/impl/build.gradle.kts | 1 + .../securebackup/impl/reset/ResetIdentityFlowNode.kt | 5 ++++- libraries/matrix/impl/build.gradle.kts | 1 + .../impl/auth/RustMatrixAuthenticationService.kt | 8 ++++++++ .../libraries/wellknown/api/ElementWellKnown.kt | 1 + .../wellknown/impl/InternalElementWellKnown.kt | 2 ++ .../element/android/libraries/wellknown/impl/Mapper.kt | 1 + .../android/features/wellknown/test/Fixtures.kt | 2 ++ 19 files changed, 60 insertions(+), 7 deletions(-) diff --git a/enterprise b/enterprise index cdde60c158..1ed9a7e8b4 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit cdde60c158ecd0987a3ba6fd79a4617551aff463 +Subproject commit 1ed9a7e8b4406a5e6d12f603cfc2083e84f8576f diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 65fe3fe087..92d8b9b646 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.Flow interface EnterpriseService { val isEnterpriseBuild: Boolean suspend fun isEnterpriseUser(sessionId: SessionId): Boolean + suspend fun tweakMasUrl(url: String, homeserver: String): String fun defaultHomeserverList(): List suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt index 6bd6c78de5..f87dc743e8 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/SessionEnterpriseService.kt @@ -10,6 +10,7 @@ package io.element.android.features.enterprise.api interface SessionEnterpriseService { suspend fun isElementCallAvailable(): Boolean + suspend fun tweakMasUrl(url: String): String suspend fun init() } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 932d082fd9..6e3ed5d3cc 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -23,7 +23,7 @@ class DefaultEnterpriseService : EnterpriseService { override val isEnterpriseBuild = false override suspend fun isEnterpriseUser(sessionId: SessionId) = false - + override suspend fun tweakMasUrl(url: String, homeserver: String) = url override fun defaultHomeserverList(): List = emptyList() override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultSessionEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultSessionEnterpriseService.kt index 3441063a8a..9aafcd343c 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultSessionEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultSessionEnterpriseService.kt @@ -15,5 +15,6 @@ import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultSessionEnterpriseService : SessionEnterpriseService { override suspend fun init() = Unit + override suspend fun tweakMasUrl(url: String): String = url override suspend fun isElementCallAvailable(): Boolean = true } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 3c17a4de7c..805c75be6a 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -30,6 +30,7 @@ class FakeEnterpriseService( private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, + private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() }, ) : EnterpriseService { private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) @@ -38,6 +39,10 @@ class FakeEnterpriseService( isEnterpriseUserResult(sessionId) } + override suspend fun tweakMasUrl(url: String, homeserver: String): String = simulateLongTask { + tweakMasUrlResult(url, homeserver) + } + override fun defaultHomeserverList(): List { return defaultHomeserverListResult() } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeSessionEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeSessionEnterpriseService.kt index 3914c60155..0bcad13033 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeSessionEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeSessionEnterpriseService.kt @@ -14,10 +14,15 @@ import io.element.android.tests.testutils.simulateLongTask class FakeSessionEnterpriseService( private val isElementCallAvailableResult: () -> Boolean = { lambdaError() }, + private val tweakMasUrlResult: (String) -> String = { lambdaError() }, ) : SessionEnterpriseService { override suspend fun init() { } + override suspend fun tweakMasUrl(url: String): String = simulateLongTask { + tweakMasUrlResult(url) + } + override suspend fun isElementCallAvailable(): Boolean = simulateLongTask { isElementCallAvailableResult() } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index 4ccdb6b387..9674d213f9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -26,6 +26,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme +import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint import io.element.android.features.linknewdevice.impl.screens.confirmation.CodeConfirmationNode import io.element.android.features.linknewdevice.impl.screens.desktop.DesktopNoticeNode @@ -65,6 +66,7 @@ class LinkNewDeviceFlowNode( private val sessionCoroutineScope: CoroutineScope, private val linkNewMobileHandler: LinkNewMobileHandler, private val linkNewDesktopHandler: LinkNewDesktopHandler, + private val sessionEnterpriseService: SessionEnterpriseService, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -301,8 +303,12 @@ class LinkNewDeviceFlowNode( } } - private fun navigateToBrowser(url: String) { - activity?.openUrlInChromeCustomTab(null, darkTheme, url) + private suspend fun navigateToBrowser(url: String) { + activity?.openUrlInChromeCustomTab( + session = null, + darkTheme = darkTheme, + url = sessionEnterpriseService.tweakMasUrl(url), + ) } @Composable diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/DefaultLinkNewDeviceEntryPointTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/DefaultLinkNewDeviceEntryPointTest.kt index 2957a89495..1b2af8f4c3 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/DefaultLinkNewDeviceEntryPointTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/DefaultLinkNewDeviceEntryPointTest.kt @@ -11,6 +11,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.test.FakeSessionEnterpriseService import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.tests.testutils.lambda.lambdaError @@ -37,6 +38,7 @@ class DefaultLinkNewDeviceEntryPointTest { sessionCoroutineScope = backgroundScope, linkNewMobileHandler = LinkNewMobileHandler(client), linkNewDesktopHandler = LinkNewDesktopHandler(client), + sessionEnterpriseService = FakeSessionEnterpriseService(), ) } val callback: LinkNewDeviceEntryPoint.Callback = object : LinkNewDeviceEntryPoint.Callback { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index 43da681a37..f407149007 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject +import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.logout.api.direct.DirectLogoutState import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider import io.element.android.features.rageshake.api.RageshakeFeatureAvailability @@ -55,6 +56,7 @@ class PreferencesRootPresenter( private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, private val featureFlagService: FeatureFlagService, private val sessionStore: SessionStore, + private val sessionEnterpriseService: SessionEnterpriseService, ) : Presenter { @Composable override fun present(): PreferencesRootState { @@ -158,6 +160,10 @@ class PreferencesRootPresenter( private fun CoroutineScope.initAccountManagementUrl( accountManagementUrl: MutableState, ) = launch { - accountManagementUrl.value = matrixClient.getAccountManagementUrl(null).getOrNull() + accountManagementUrl.value = matrixClient.getAccountManagementUrl(null) + .getOrNull() + ?.let { + sessionEnterpriseService.tweakMasUrl(it) + } } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index d324f5eb3a..a6861be12d 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -12,6 +12,8 @@ package io.element.android.features.preferences.impl.root import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.api.SessionEnterpriseService +import io.element.android.features.enterprise.test.FakeSessionEnterpriseService import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider import io.element.android.features.rageshake.api.RageshakeFeatureAvailability @@ -65,6 +67,9 @@ class PreferencesRootPresenterTest { ) createPresenter( matrixClient = matrixClient, + sessionEnterpriseService = FakeSessionEnterpriseService( + tweakMasUrlResult = { "tweaked $it" }, + ), ).test { val initialState = awaitItem() assertThat(initialState.myUser).isEqualTo( @@ -100,7 +105,7 @@ class PreferencesRootPresenterTest { val finalState = awaitItem() accountManagementUrlResult.assertions().isCalledOnce() .with(value(null)) - assertThat(finalState.accountManagementUrl).isEqualTo("null url") + assertThat(finalState.accountManagementUrl).isEqualTo("tweaked null url") } } @@ -327,6 +332,7 @@ class PreferencesRootPresenterTest { indicatorService: IndicatorService = FakeIndicatorService(), featureFlagService: FeatureFlagService = FakeFeatureFlagService(), sessionStore: SessionStore = InMemorySessionStore(), + sessionEnterpriseService: SessionEnterpriseService = FakeSessionEnterpriseService(), ) = PreferencesRootPresenter( matrixClient = matrixClient, sessionVerificationService = sessionVerificationService, @@ -339,5 +345,6 @@ class PreferencesRootPresenterTest { rageshakeFeatureAvailability = rageshakeFeatureAvailability, featureFlagService = featureFlagService, sessionStore = sessionStore, + sessionEnterpriseService = sessionEnterpriseService, ) } diff --git a/features/securebackup/impl/build.gradle.kts b/features/securebackup/impl/build.gradle.kts index 82f30fa5ce..54d87ef22e 100644 --- a/features/securebackup/impl/build.gradle.kts +++ b/features/securebackup/impl/build.gradle.kts @@ -28,6 +28,7 @@ setupDependencyInjection() dependencies { implementation(projects.appconfig) + implementation(projects.features.enterprise.api) implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt index 4e2284890f..7dd6bf632a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt @@ -25,6 +25,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme +import io.element.android.features.enterprise.api.SessionEnterpriseService import io.element.android.features.securebackup.impl.reset.password.ResetIdentityPasswordNode import io.element.android.features.securebackup.impl.reset.root.ResetIdentityRootNode import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab @@ -53,6 +54,7 @@ class ResetIdentityFlowNode( private val resetIdentityFlowManager: ResetIdentityFlowManager, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, + private val sessionEnterpriseService: SessionEnterpriseService, ) : BaseFlowNode( backstack = BackStack(initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap), buildContext = buildContext, @@ -125,7 +127,8 @@ class ResetIdentityFlowNode( } is IdentityOAuthResetHandle -> { Timber.d("Launching reset confirmation in MAS") - activity.openUrlInChromeCustomTab(null, darkTheme, handle.url) + val url = sessionEnterpriseService.tweakMasUrl(handle.url) + activity.openUrlInChromeCustomTab(null, darkTheme, url) Timber.d("Starting resetOAuth") resetJob = launch { handle.resetOAuth() } resetJob?.invokeOnCompletion { Timber.d("resetOAuth ended") } diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 67386cc592..7dcc835781 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(projects.libraries.rustlsTls) implementation(projects.appconfig) + implementation(projects.features.enterprise.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.di) implementation(projects.libraries.featureflag.api) 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 79793e4e77..6cd30d4f70 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 @@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.auth import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -66,6 +67,7 @@ class RustMatrixAuthenticationService( private val rustMatrixClientFactory: RustMatrixClientFactory, private val passphraseGenerator: PassphraseGenerator, private val oAuthConfigurationProvider: OAuthConfigurationProvider, + private val enterpriseService: EnterpriseService, ) : MatrixAuthenticationService { // Any existing Element Classic session that we want to try to import secrets from during login. private var elementClassicSession: ElementClassicSession? = null @@ -269,6 +271,12 @@ class RustMatrixAuthenticationService( additionalScopes = emptyList(), ) val url = oAuthAuthorizationData.loginUrl() + .let { + enterpriseService.tweakMasUrl( + url = it, + homeserver = client.server() ?: client.homeserver(), + ) + } pendingOAuthAuthorizationData = oAuthAuthorizationData OAuthDetails(url) }.mapFailure { failure -> diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt index 134a9bcdb5..4c1c476c7a 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt @@ -14,4 +14,5 @@ data class ElementWellKnown( val rageshakeUrl: String?, val brandColor: String?, val notificationSound: String?, + val identityProviderAppScheme: String?, ) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt index d4661d1be0..2e2b5de16f 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt @@ -32,4 +32,6 @@ data class InternalElementWellKnown( val brandColor: String? = null, @SerialName("notification_sound") val notificationSound: String? = null, + @SerialName("idp_app_scheme") + val identityProviderAppScheme: String? = null, ) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt index c7ca088e68..41ed54d7db 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt @@ -16,4 +16,5 @@ internal fun InternalElementWellKnown.map() = ElementWellKnown( rageshakeUrl = rageshakeUrl, brandColor = brandColor, notificationSound = notificationSound, + identityProviderAppScheme = identityProviderAppScheme, ) diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt index 7457aafc98..ae7d1c629c 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt @@ -16,10 +16,12 @@ fun anElementWellKnown( rageshakeUrl: String? = null, brandColor: String? = null, notificationSound: String? = null, + identityProviderAppScheme: String? = null, ) = ElementWellKnown( registrationHelperUrl = registrationHelperUrl, enforceElementPro = enforceElementPro, rageshakeUrl = rageshakeUrl, brandColor = brandColor, notificationSound = notificationSound, + identityProviderAppScheme = identityProviderAppScheme, ) From c1e908a8e64e2699bc70a05265bdfd3122cfa876 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 09:19:41 +0200 Subject: [PATCH 036/471] Fix tests. --- libraries/matrix/impl/build.gradle.kts | 1 + .../matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt | 4 ++++ .../wellknown/impl/DefaultSessionWellknownRetrieverTest.kt | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 7dcc835781..32bbeb082a 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.kotlinx.collections.immutable) testCommonDependencies(libs) + testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.preferences.test) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt index bff2711bc5..7f422acfcf 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt @@ -9,6 +9,8 @@ package io.element.android.libraries.matrix.impl.auth import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.matrix.impl.ClientBuilderProvider import io.element.android.libraries.matrix.impl.FakeClientBuilderProvider import io.element.android.libraries.matrix.impl.createRustMatrixClientFactory @@ -50,6 +52,7 @@ class RustMatrixAuthenticationServiceTest { private fun TestScope.createRustMatrixAuthenticationService( sessionStore: SessionStore = InMemorySessionStore(), clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(), + enterpriseService: EnterpriseService = FakeEnterpriseService(), ): RustMatrixAuthenticationService { val baseDirectory = File("/base") val cacheDirectory = File("/cache") @@ -68,6 +71,7 @@ class RustMatrixAuthenticationServiceTest { buildMeta = aBuildMeta(), oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), ), + enterpriseService = enterpriseService, ) } } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index faad139a36..8a230c0317 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -36,6 +36,7 @@ class DefaultSessionWellknownRetrieverTest { rageshakeUrl = null, brandColor = null, notificationSound = null, + identityProviderAppScheme = null, ) ) ) @@ -53,7 +54,8 @@ class DefaultSessionWellknownRetrieverTest { "enforce_element_pro": true, "rageshake_url": "a_rageshake_url", "brand_color": "#FF0000", - "notification_sound": "a_notification_sound.flac" + "notification_sound": "a_notification_sound.flac", + "idp_app_scheme": "an_app_scheme" }""".trimIndent().toByteArray() ) } @@ -66,6 +68,7 @@ class DefaultSessionWellknownRetrieverTest { rageshakeUrl = "a_rageshake_url", brandColor = "#FF0000", notificationSound = "a_notification_sound.flac", + identityProviderAppScheme = "an_app_scheme", ) ) ) @@ -93,6 +96,7 @@ class DefaultSessionWellknownRetrieverTest { rageshakeUrl = "a_rageshake_url", brandColor = null, notificationSound = null, + identityProviderAppScheme = null, ) ) ) From 29e0a08dd93e4df5d7fce4fbf456cab738b274c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 16:39:07 +0200 Subject: [PATCH 037/471] Add a CacheStore module. --- libraries/cachestore/api/build.gradle.kts | 13 ++++ .../libraries/cachestore/api/CacheData.kt | 13 ++++ .../libraries/cachestore/api/CacheStore.kt | 14 ++++ libraries/cachestore/impl/build.gradle.kts | 48 +++++++++++++ .../cachestore/impl/CacheDataMapper.kt | 27 +++++++ .../cachestore/impl/DatabaseCacheStore.kt | 36 ++++++++++ .../cachestore/impl/di/CacheStoreModule.kt | 43 +++++++++++ .../impl/src/main/sqldelight/databases/1.db | Bin 0 -> 12288 bytes .../android/libraries/cachestore/CacheData.sq | 26 +++++++ .../impl/DatabaseCacheStoreTest.kt | 68 ++++++++++++++++++ .../libraries/sessionstorage/impl/Fixtures.kt | 21 ++++++ libraries/cachestore/test/build.gradle.kts | 17 +++++ .../sessionstorage/test/CacheData.kt | 18 +++++ .../sessionstorage/test/InMemoryCacheStore.kt | 29 ++++++++ .../kotlin/extension/DependencyHandleScope.kt | 1 + 15 files changed, 374 insertions(+) create mode 100644 libraries/cachestore/api/build.gradle.kts create mode 100644 libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheData.kt create mode 100644 libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt create mode 100644 libraries/cachestore/impl/build.gradle.kts create mode 100644 libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt create mode 100644 libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt create mode 100644 libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt create mode 100644 libraries/cachestore/impl/src/main/sqldelight/databases/1.db create mode 100644 libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq create mode 100644 libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt create mode 100644 libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt create mode 100644 libraries/cachestore/test/build.gradle.kts create mode 100644 libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt create mode 100644 libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt diff --git a/libraries/cachestore/api/build.gradle.kts b/libraries/cachestore/api/build.gradle.kts new file mode 100644 index 0000000000..0e03bb5136 --- /dev/null +++ b/libraries/cachestore/api/build.gradle.kts @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.cachestore.api" +} diff --git a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheData.kt b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheData.kt new file mode 100644 index 0000000000..a448ba7df8 --- /dev/null +++ b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheData.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.cachestore.api + +data class CacheData( + val value: String, + val updatedAt: Long, +) diff --git a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt new file mode 100644 index 0000000000..f16a663ed7 --- /dev/null +++ b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.cachestore.api + +interface CacheStore { + suspend fun storeData(key: String, data: CacheData) + suspend fun getData(key: String): CacheData? + suspend fun deleteData(key: String) +} diff --git a/libraries/cachestore/impl/build.gradle.kts b/libraries/cachestore/impl/build.gradle.kts new file mode 100644 index 0000000000..f0c7ba237c --- /dev/null +++ b/libraries/cachestore/impl/build.gradle.kts @@ -0,0 +1,48 @@ +import extension.setupDependencyInjection +import extension.testCommonDependencies + +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") + alias(libs.plugins.sqldelight) +} + +android { + namespace = "io.element.android.libraries.cachestore.impl" +} + +setupDependencyInjection() + +dependencies { + implementation(projects.libraries.androidutils) + implementation(projects.libraries.core) + implementation(projects.libraries.encryptedDb) + api(projects.libraries.cachestore.api) + implementation(libs.sqldelight.driver.android) + implementation(libs.sqlcipher) + implementation(libs.sqlite) + implementation(projects.libraries.di) + implementation(libs.sqldelight.coroutines) + + testCommonDependencies(libs) + testImplementation(libs.sqldelight.driver.jvm) +} + +sqldelight { + databases { + create("CacheDatabase") { + // https://sqldelight.github.io/sqldelight/2.1.0/android_sqlite/migrations/ + // To generate a .db file from your latest schema, run this task + // ./gradlew generateDebugCacheDatabaseSchema + // Test migration by running + // ./gradlew verifySqlDelightMigration + schemaOutputDirectory = File("src/main/sqldelight/databases") + verifyMigrations = true + } + } +} diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt new file mode 100644 index 0000000000..7e67b6de72 --- /dev/null +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.cachestore.impl + +import io.element.android.libraries.cachestore.api.CacheData +import java.util.Date +import io.element.android.libraries.cachestore.CacheData as DbCacheData + +internal fun CacheData.toDbModel(key: String): DbCacheData { + return DbCacheData( + key = key, + value_ = value, + updatedAt = updatedAt.time, + ) +} + +internal fun DbCacheData.toApiModel(): CacheData { + return CacheData( + value = value_, + updatedAt = Date(updatedAt), + ) +} diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt new file mode 100644 index 0000000000..126387da11 --- /dev/null +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.cachestore.impl + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.SingleIn +import io.element.android.libraries.cachestore.api.CacheData +import io.element.android.libraries.cachestore.api.CacheStore + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class DatabaseCacheStore( + private val database: CacheDatabase, +) : CacheStore { + override suspend fun getData(key: String): CacheData? { + return database.cacheDataQueries.selectData(key) + .executeAsOneOrNull() + ?.toApiModel() + } + + override suspend fun storeData(key: String, data: CacheData) { + database.cacheDataQueries.insertData( + data.toDbModel(key) + ) + } + + override suspend fun deleteData(key: String) { + database.cacheDataQueries.deleteData(key) + } +} diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt new file mode 100644 index 0000000000..05fa3d9d97 --- /dev/null +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.cachestore.impl.di + +import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.BindingContainer +import dev.zacsweers.metro.ContributesTo +import dev.zacsweers.metro.Provides +import dev.zacsweers.metro.SingleIn +import io.element.android.libraries.cachestore.impl.CacheDatabase +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.encrypteddb.SqlCipherDriverFactory +import io.element.encrypteddb.passphrase.RandomSecretPassphraseProvider + +@BindingContainer +@ContributesTo(AppScope::class) +object CacheStoreModule { + @Provides + @SingleIn(AppScope::class) + fun provideCacheDatabase( + @ApplicationContext context: Context, + ): CacheDatabase { + val name = "cache_database" + val secretFile = context.getDatabasePath("$name.key") + + // Make sure the parent directory of the key file exists, otherwise it will crash in older Android versions + val parentDir = secretFile.parentFile + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs() + } + + val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile) + val driver = SqlCipherDriverFactory(passphraseProvider) + .create(CacheDatabase.Schema, "$name.db", context) + return CacheDatabase(driver) + } +} diff --git a/libraries/cachestore/impl/src/main/sqldelight/databases/1.db b/libraries/cachestore/impl/src/main/sqldelight/databases/1.db new file mode 100644 index 0000000000000000000000000000000000000000..8e4b0cac72d8a31cf1c7038ebce700b66768eaa2 GIT binary patch literal 12288 zcmeI#&r8EF6bJC6ib`R02Vuv);{+)T{{ia^GOU}{2<}v(jX12%AL-QN{s8|h|0ltV zNo4^~I|}3b;H7y@TAEK!@BA#A7)>|R=X+&nkF^=+Y@diR#%-?IT!gFF7lpmLdRcIm ze0;Z}FBU`zi<0Pb*#lUI00bZa0SG_<0uX=z1Rwwb2>idme4B4};+W50jk>CIUtQno zLuHho1pQp3Qjkm!GC}?}bwjdor{776OG(3#&5JCf(|mA}=3_b*gQ5Rl)!&PAK>z{}fB*y_009U<00Izz00bI= EA429uc>n+a literal 0 HcmV?d00001 diff --git a/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq new file mode 100644 index 0000000000..4683d9aa7d --- /dev/null +++ b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq @@ -0,0 +1,26 @@ +-------------------------------------------------------------------- +-- Current version of the DB is the highest value of filename +-- in the folder `sqldelight/databases`. +-- +-- When upgrading the schema, you have to create a file .sqm in the +-- `sqldelight/databases` folder and run the following task to +-- generate a .db file using the latest schema +-- > ./gradlew generateDebugCacheDatabaseSchema +-------------------------------------------------------------------- + +CREATE TABLE CacheData ( + key TEXT NOT NULL PRIMARY KEY, + value TEXT NOT NULL, + updatedAt INTEGER NOT NULL +); + + +selectData: +SELECT * FROM CacheData WHERE key = ?; + +-- insert or update data by key +insertData: +INSERT INTO CacheData VALUES ? ON CONFLICT(key) DO UPDATE SET value = excluded.value, updatedAt = excluded.updatedAt; + +deleteData: +DELETE FROM CacheData WHERE key = ?; diff --git a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt new file mode 100644 index 0000000000..a2216daa06 --- /dev/null +++ b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.sessionstorage.impl + +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.cachestore.api.CacheData +import io.element.android.libraries.cachestore.impl.CacheDatabase +import io.element.android.libraries.cachestore.impl.DatabaseCacheStore +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import io.element.android.libraries.cachestore.CacheData as DbCacheData + +private const val A_KEY = "aKey" +private const val A_DATA_1 = "aData1" +private const val A_DATA_2 = "aData2" + +class DatabaseCacheStoreTest { + private lateinit var database: CacheDatabase + private lateinit var databaseCacheStore: DatabaseCacheStore + + @OptIn(ExperimentalCoroutinesApi::class) + @Before + fun setup() { + // Initialise in memory SQLite driver + val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + CacheDatabase.Schema.create(driver) + + database = CacheDatabase(driver) + databaseCacheStore = DatabaseCacheStore( + database = database, + ) + } + + @Test + fun `storeData persists the CacheData into the DB, deleteData deletes it`() = runTest { + // Assert that no data is stored for the key + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() + // Store data + databaseCacheStore.storeData(A_KEY, CacheData(A_DATA_1, 1)) + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isEqualTo( + DbCacheData( + key = A_KEY, + value_ = A_DATA_1, + updatedAt = 1, + ) + ) + // Update data + databaseCacheStore.storeData(A_KEY, CacheData(A_DATA_2, 2)) + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isEqualTo( + DbCacheData( + key = A_KEY, + value_ = A_DATA_2, + updatedAt = 2, + ) + ) + // Delete data + databaseCacheStore.deleteData(A_KEY) + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() + } +} diff --git a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt new file mode 100644 index 0000000000..3dca9efaf3 --- /dev/null +++ b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.sessionstorage.impl + +import io.element.android.libraries.cachestore.CacheData +import java.util.Date + +internal fun aCacheData( + key: String = "aKey", + value: String = "aValue", + updatedAt: Date = Date(), +) = CacheData( + key = key, + value_ = value, + updatedAt = updatedAt.time, +) diff --git a/libraries/cachestore/test/build.gradle.kts b/libraries/cachestore/test/build.gradle.kts new file mode 100644 index 0000000000..7ad2ac48d9 --- /dev/null +++ b/libraries/cachestore/test/build.gradle.kts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.cachestore.test" +} + +dependencies { + implementation(projects.libraries.cachestore.api) +} diff --git a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt new file mode 100644 index 0000000000..30633e8ff9 --- /dev/null +++ b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.sessionstorage.test + +import io.element.android.libraries.cachestore.api.CacheData + +fun aCacheData( + value: String = "aValue", + updatedAt: Long = 0, +) = CacheData( + value = value, + updatedAt = updatedAt, +) diff --git a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt new file mode 100644 index 0000000000..b0f77062ba --- /dev/null +++ b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.sessionstorage.test + +import io.element.android.libraries.cachestore.api.CacheData +import io.element.android.libraries.cachestore.api.CacheStore + +class InMemoryCacheStore( + initialData: Map = emptyMap(), +) : CacheStore { + val dataMap = initialData.toMutableMap() + + override suspend fun storeData(key: String, data: CacheData) { + dataMap[key] = data + } + + override suspend fun getData(key: String): CacheData? { + return dataMap[key] + } + + override suspend fun deleteData(key: String) { + dataMap.remove(key) + } +} diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index f832136083..3e01ffc25f 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -104,6 +104,7 @@ fun DependencyHandlerScope.allLibrariesImpl() { implementation(project(":libraries:architecture")) implementation(project(":libraries:dateformatter:impl")) implementation(project(":libraries:di")) + implementation(project(":libraries:cachestore:impl")) implementation(project(":libraries:session-storage:impl")) implementation(project(":libraries:mediapickers:impl")) implementation(project(":libraries:mediaupload:impl")) From 8512279d9619d97370984e1dd426cee5d6e0e2ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 22:05:14 +0200 Subject: [PATCH 038/471] Add a cache for Element .well-known file. --- libraries/wellknown/impl/build.gradle.kts | 4 + .../impl/DefaultSessionWellknownRetriever.kt | 48 ++++++- .../DefaultSessionWellknownRetrieverTest.kt | 132 ++++++++++++++++-- 3 files changed, 173 insertions(+), 11 deletions(-) diff --git a/libraries/wellknown/impl/build.gradle.kts b/libraries/wellknown/impl/build.gradle.kts index f803eeec3c..1e2c4d7d61 100644 --- a/libraries/wellknown/impl/build.gradle.kts +++ b/libraries/wellknown/impl/build.gradle.kts @@ -33,9 +33,13 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.network) + implementation(projects.libraries.cachestore.api) + implementation(projects.services.toolbox.api) testCommonDependencies(libs) testImplementation(libs.coroutines.core) + testImplementation(projects.libraries.cachestore.test) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.wellknown.test) testImplementation(projects.services.toolbox.test) } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt index 3bcf9bf573..a0223e93cc 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt @@ -10,29 +10,70 @@ package io.element.android.libraries.wellknown.impl import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.androidutils.json.JsonProvider +import io.element.android.libraries.cachestore.api.CacheData +import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.SessionWellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult +import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import timber.log.Timber @ContributesBinding(SessionScope::class) class DefaultSessionWellknownRetriever( private val matrixClient: MatrixClient, private val json: JsonProvider, + private val cacheStore: CacheStore, + private val systemClock: SystemClock, + @SessionCoroutineScope + private val sessionCoroutineScope: CoroutineScope, ) : SessionWellknownRetriever { private val domain by lazy { matrixClient.userIdServerName() } override suspend fun getElementWellKnown(): WellknownRetrieverResult { val url = "https://$domain/.well-known/element/element.json" + val cacheData = cacheStore.getData(url) + if (cacheData != null) { + Timber.d("Element .well-known data retrieved from cache for $domain") + // If the cache is outdated, trigger a refresh in background but still return the cached value + if (systemClock.epochMillis() > cacheData.updatedAt + CACHE_VALIDITY_MILLIS) { + sessionCoroutineScope.launch { + fetchElementWellKnown(url) + } + } + try { + val parsed = json().decodeFromString(cacheData.value).map() + return WellknownRetrieverResult.Success(parsed) + } catch (e: Exception) { + Timber.e(e, "Failed to parse cached Element .well-known data for $domain, deleting cache") + cacheStore.deleteData(url) + } + } + + return fetchElementWellKnown(url) + } + + private suspend fun fetchElementWellKnown(url: String): WellknownRetrieverResult { return matrixClient .getUrl(url) .mapCatchingExceptions { val data = String(it) - json().decodeFromString(data).map() + val parsed = json().decodeFromString(data).map() + // Also store in cache, if valid + cacheStore.storeData( + key = url, + data = CacheData( + value = data, + updatedAt = systemClock.epochMillis(), + ) + ) + parsed } .toWellknownRetrieverResult() } @@ -51,4 +92,9 @@ class DefaultSessionWellknownRetriever( } } ) + + companion object { + // 1 day + private const val CACHE_VALIDITY_MILLIS = 1 * 24 * 60 * 60 * 1000L + } } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index 8a230c0317..2824a7e112 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -6,16 +6,30 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.libraries.wellknown.impl import com.google.common.truth.Truth.assertThat +import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.androidutils.json.DefaultJsonProvider +import io.element.android.libraries.androidutils.json.JsonProvider +import io.element.android.libraries.cachestore.api.CacheData +import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult +import io.element.android.services.toolbox.api.systemclock.SystemClock +import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -49,14 +63,7 @@ class DefaultSessionWellknownRetrieverTest { val sut = createDefaultSessionWellknownRetriever( getUrlLambda = { Result.success( - """{ - "registration_helper_url": "a_registration_url", - "enforce_element_pro": true, - "rageshake_url": "a_rageshake_url", - "brand_color": "#FF0000", - "notification_sound": "a_notification_sound.flac", - "idp_app_scheme": "an_app_scheme" - }""".trimIndent().toByteArray() + WELLKNOWN_CONTENT.toByteArray() ) } ) @@ -127,13 +134,118 @@ class DefaultSessionWellknownRetrieverTest { assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) } - private fun createDefaultSessionWellknownRetriever( + @Test + fun `get element wellknown hitting cache`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { lambdaError() }, + cacheStore = InMemoryCacheStore( + initialData = mapOf( + WELLKNOWN_URL to CacheData( + value = WELLKNOWN_CONTENT, + updatedAt = A_FAKE_TIMESTAMP, + ) + ) + ) + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success( + ElementWellKnown( + registrationHelperUrl = "a_registration_url", + enforceElementPro = true, + rageshakeUrl = "a_rageshake_url", + brandColor = "#FF0000", + notificationSound = "a_notification_sound.flac", + identityProviderAppScheme = "an_app_scheme", + ) + ) + ) + } + + @Test + fun `get element wellknown hitting cache containing invalid json`() = runTest { + val cacheStore = InMemoryCacheStore( + initialData = mapOf( + WELLKNOWN_URL to CacheData( + value = WELLKNOWN_CONTENT, + updatedAt = A_FAKE_TIMESTAMP, + ) + ) + ) + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success("{}".toByteArray()) + }, + cacheStore = cacheStore, + jsonProvider = JsonProvider { throw Exception("Failed to parse JSON") } + ) + assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) + // Ensure that the cache is deleted after the failure to parse it + assertThat(cacheStore.dataMap).isEmpty() + } + + @Test + fun `get element wellknown hitting outdated cache`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success("{}".toByteArray()) + }, + cacheStore = InMemoryCacheStore( + initialData = mapOf( + WELLKNOWN_URL to CacheData( + value = WELLKNOWN_CONTENT, + updatedAt = 0L, + ) + ), + ), + // 3 days later, so the cache is outdated + systemClock = FakeSystemClock(3 * 24 * 60 * 60 * 1000L) + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success( + ElementWellKnown( + registrationHelperUrl = "a_registration_url", + enforceElementPro = true, + rageshakeUrl = "a_rageshake_url", + brandColor = "#FF0000", + notificationSound = "a_notification_sound.flac", + identityProviderAppScheme = "an_app_scheme", + ) + ) + ) + // Next call returns the updated value + runCurrent() + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success( + anElementWellKnown() + ) + ) + } + + private fun TestScope.createDefaultSessionWellknownRetriever( getUrlLambda: (String) -> Result, + jsonProvider: JsonProvider = DefaultJsonProvider(), + cacheStore: CacheStore = InMemoryCacheStore(), + systemClock: SystemClock = FakeSystemClock(), ) = DefaultSessionWellknownRetriever( matrixClient = FakeMatrixClient( userIdServerNameLambda = { "user.domain.org" }, getUrlLambda = getUrlLambda, ), - json = DefaultJsonProvider(), + json = jsonProvider, + cacheStore = cacheStore, + systemClock = systemClock, + sessionCoroutineScope = backgroundScope, ) + + companion object { + private const val WELLKNOWN_URL = "https://user.domain.org/.well-known/element/element.json" + private const val WELLKNOWN_CONTENT = """{ + "registration_helper_url": "a_registration_url", + "enforce_element_pro": true, + "rageshake_url": "a_rageshake_url", + "brand_color": "#FF0000", + "notification_sound": "a_notification_sound.flac", + "idp_app_scheme": "an_app_scheme" + }""" + } } From 35a3a0ba063cf85f1cbe3bc4d0b1a8d8955c0066 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 22:43:42 +0200 Subject: [PATCH 039/471] Fix compilation issue. --- .../android/libraries/cachestore/impl/CacheDataMapper.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt index 7e67b6de72..2a33ce4fda 100644 --- a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/CacheDataMapper.kt @@ -8,20 +8,19 @@ package io.element.android.libraries.cachestore.impl import io.element.android.libraries.cachestore.api.CacheData -import java.util.Date import io.element.android.libraries.cachestore.CacheData as DbCacheData internal fun CacheData.toDbModel(key: String): DbCacheData { return DbCacheData( key = key, value_ = value, - updatedAt = updatedAt.time, + updatedAt = updatedAt, ) } internal fun DbCacheData.toApiModel(): CacheData { return CacheData( value = value_, - updatedAt = Date(updatedAt), + updatedAt = updatedAt, ) } From 3dd2a60b3a1994a6dbd8f246460604bb71ba08e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 29 Apr 2026 22:51:54 +0200 Subject: [PATCH 040/471] Do not use generic Exception --- .../wellknown/impl/DefaultSessionWellknownRetrieverTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index 2824a7e112..5b1cebd6cf 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -176,7 +176,7 @@ class DefaultSessionWellknownRetrieverTest { Result.success("{}".toByteArray()) }, cacheStore = cacheStore, - jsonProvider = JsonProvider { throw Exception("Failed to parse JSON") } + jsonProvider = JsonProvider { error("Failed to parse JSON") } ) assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) // Ensure that the cache is deleted after the failure to parse it From c8773c890f2fe88d7075cc5f8ea37c6eb1be44c2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 08:49:56 +0200 Subject: [PATCH 041/471] Fix SQL query --- .../io/element/android/libraries/cachestore/CacheData.sq | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq index 4683d9aa7d..aa8bacf90b 100644 --- a/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq +++ b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq @@ -18,9 +18,8 @@ CREATE TABLE CacheData ( selectData: SELECT * FROM CacheData WHERE key = ?; --- insert or update data by key insertData: -INSERT INTO CacheData VALUES ? ON CONFLICT(key) DO UPDATE SET value = excluded.value, updatedAt = excluded.updatedAt; +INSERT OR REPLACE INTO CacheData VALUES ?; deleteData: DELETE FROM CacheData WHERE key = ?; From b50969437df63935db744977d7c09bbf57093500 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 09:01:35 +0200 Subject: [PATCH 042/471] Ensure clearing the cache delete the CacheStore. --- features/preferences/impl/build.gradle.kts | 2 ++ .../impl/tasks/ClearCacheUseCase.kt | 4 ++++ .../impl/tasks/DefaultClearCacheUseCaseTest.kt | 7 +++++++ .../libraries/cachestore/api/CacheStore.kt | 1 + .../cachestore/impl/DatabaseCacheStore.kt | 8 ++++++-- .../android/libraries/cachestore/CacheData.sq | 3 +++ .../impl/DatabaseCacheStoreTest.kt | 18 ++++++++++++++++++ .../sessionstorage/test/InMemoryCacheStore.kt | 4 ++++ 8 files changed, 45 insertions(+), 2 deletions(-) diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index ad28c90966..33936760f5 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { implementation(projects.appconfig) implementation(projects.libraries.core) implementation(projects.libraries.architecture) + implementation(projects.libraries.cachestore.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.featureflag.api) @@ -114,6 +115,7 @@ dependencies { testImplementation(projects.features.logout.test) testImplementation(projects.libraries.indicator.test) testImplementation(projects.libraries.pushproviders.test) + testImplementation(projects.libraries.cachestore.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.services.appnavstate.impl) testImplementation(projects.services.analytics.test) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt index 6c26866e93..141adafe2b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt @@ -14,6 +14,7 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Provider import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.preferences.impl.DefaultCacheService +import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.ApplicationContext @@ -37,8 +38,11 @@ class DefaultClearCacheUseCase( private val pushService: PushService, private val seenInvitesStore: SeenInvitesStore, private val activeRoomsHolder: ActiveRoomsHolder, + private val cacheStore: CacheStore, ) : ClearCacheUseCase { override suspend fun invoke() = withContext(coroutineDispatchers.io) { + // Clear cache store + cacheStore.deleteAll() // Active rooms should be disposed of before clearing the cache activeRoomsHolder.clear(matrixClient.sessionId) // Clear Matrix cache diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/tasks/DefaultClearCacheUseCaseTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/tasks/DefaultClearCacheUseCaseTest.kt index 6845ecb3a4..1c1cb83def 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/tasks/DefaultClearCacheUseCaseTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/tasks/DefaultClearCacheUseCaseTest.kt @@ -19,6 +19,8 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.push.test.FakePushService +import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore +import io.element.android.libraries.sessionstorage.test.aCacheData import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -49,6 +51,9 @@ class DefaultClearCacheUseCaseTest { ) val seenInvitesStore = InMemorySeenInvitesStore(setOf(A_ROOM_ID)) assertThat(seenInvitesStore.seenRoomIds().first()).isNotEmpty() + val cacheStore = InMemoryCacheStore( + initialData = mapOf("key1" to aCacheData()) + ) val sut = DefaultClearCacheUseCase( context = InstrumentationRegistry.getInstrumentation().context, matrixClient = matrixClient, @@ -58,9 +63,11 @@ class DefaultClearCacheUseCaseTest { pushService = pushService, seenInvitesStore = seenInvitesStore, activeRoomsHolder = activeRoomsHolder, + cacheStore = cacheStore, ) defaultCacheService.clearedCacheEventFlow.test { sut.invoke() + assertThat(cacheStore.dataMap).isEmpty() clearCacheLambda.assertions().isCalledOnce() setIgnoreRegistrationErrorLambda.assertions().isCalledOnce() .with(value(matrixClient.sessionId), value(false)) diff --git a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt index f16a663ed7..5df446f688 100644 --- a/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt +++ b/libraries/cachestore/api/src/main/kotlin/io/element/android/libraries/cachestore/api/CacheStore.kt @@ -11,4 +11,5 @@ interface CacheStore { suspend fun storeData(key: String, data: CacheData) suspend fun getData(key: String): CacheData? suspend fun deleteData(key: String) + suspend fun deleteAll() } diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt index 126387da11..54766803f9 100644 --- a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStore.kt @@ -27,10 +27,14 @@ class DatabaseCacheStore( override suspend fun storeData(key: String, data: CacheData) { database.cacheDataQueries.insertData( data.toDbModel(key) - ) + ).await() } override suspend fun deleteData(key: String) { - database.cacheDataQueries.deleteData(key) + database.cacheDataQueries.deleteData(key).await() + } + + override suspend fun deleteAll() { + database.cacheDataQueries.deleteAll().await() } } diff --git a/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq index aa8bacf90b..fd350ac7ba 100644 --- a/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq +++ b/libraries/cachestore/impl/src/main/sqldelight/io/element/android/libraries/cachestore/CacheData.sq @@ -23,3 +23,6 @@ INSERT OR REPLACE INTO CacheData VALUES ?; deleteData: DELETE FROM CacheData WHERE key = ?; + +deleteAll: +DELETE FROM CacheData; diff --git a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt index a2216daa06..36d7e05532 100644 --- a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt +++ b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt @@ -65,4 +65,22 @@ class DatabaseCacheStoreTest { databaseCacheStore.deleteData(A_KEY) assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() } + + @Test + fun `deleteAll deletes all the data`() = runTest { + // Assert that no data is stored for the key + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() + // Store data + databaseCacheStore.storeData(A_KEY, CacheData(A_DATA_1, 1)) + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isEqualTo( + DbCacheData( + key = A_KEY, + value_ = A_DATA_1, + updatedAt = 1, + ) + ) + // Delete all data + databaseCacheStore.deleteAll() + assertThat(database.cacheDataQueries.selectData(A_KEY).executeAsOneOrNull()).isNull() + } } diff --git a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt index b0f77062ba..f029c8bcde 100644 --- a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt +++ b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt @@ -26,4 +26,8 @@ class InMemoryCacheStore( override suspend fun deleteData(key: String) { dataMap.remove(key) } + + override suspend fun deleteAll() { + dataMap.clear() + } } From 4d0be69b4c22945270dfceeb675410f221234ff3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 09:52:10 +0200 Subject: [PATCH 043/471] In the module `:libraries:matrix.api`, change the dependencies to: - libraries.sessionStorage.api - projects.libraries.architecture from `api` to `implementation`. Modules who need `:libraries:matrix.api` do not necessarily need to use the session storage api. --- appconfig/build.gradle.kts | 2 ++ appnav/build.gradle.kts | 1 + features/enterprise/test/build.gradle.kts | 1 + features/home/impl/build.gradle.kts | 1 + features/invite/impl/build.gradle.kts | 1 + features/invite/test/build.gradle.kts | 1 + features/location/test/build.gradle.kts | 2 +- features/logout/impl/build.gradle.kts | 1 + features/messages/test/build.gradle.kts | 1 + features/poll/test/build.gradle.kts | 1 + features/preferences/impl/build.gradle.kts | 1 + features/preferences/test/build.gradle.kts | 1 + features/signedout/impl/build.gradle.kts | 1 + libraries/matrix/api/build.gradle.kts | 4 ++-- libraries/matrix/impl/build.gradle.kts | 2 ++ libraries/matrix/test/build.gradle.kts | 1 + libraries/matrixmedia/impl/build.gradle.kts | 2 ++ libraries/mediaupload/test/build.gradle.kts | 1 + libraries/mediaviewer/test/build.gradle.kts | 1 + libraries/preferences/impl/build.gradle.kts | 1 + libraries/pushproviders/firebase/build.gradle.kts | 1 + libraries/recentemojis/impl/build.gradle.kts | 1 + libraries/session-storage/test/build.gradle.kts | 1 + libraries/voiceplayer/api/build.gradle.kts | 1 + libraries/voiceplayer/impl/build.gradle.kts | 1 + libraries/workmanager/api/build.gradle.kts | 2 +- libraries/workmanager/impl/build.gradle.kts | 1 + services/analyticsproviders/sentry/build.gradle.kts | 2 ++ 28 files changed, 33 insertions(+), 4 deletions(-) diff --git a/appconfig/build.gradle.kts b/appconfig/build.gradle.kts index 45496acb77..64b9b76a14 100644 --- a/appconfig/build.gradle.kts +++ b/appconfig/build.gradle.kts @@ -48,6 +48,8 @@ android { } dependencies { + implementation(libs.coroutines.core) implementation(libs.androidx.annotationjvm) + implementation(libs.androidx.corektx) implementation(projects.libraries.matrix.api) } diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 6be468b0d1..7440ecd2bf 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.matrixui) implementation(projects.libraries.matrixmedia.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) implementation(projects.features.login.api) diff --git a/features/enterprise/test/build.gradle.kts b/features/enterprise/test/build.gradle.kts index 542e73717a..c37fc53de3 100644 --- a/features/enterprise/test/build.gradle.kts +++ b/features/enterprise/test/build.gradle.kts @@ -15,6 +15,7 @@ android { dependencies { api(projects.features.enterprise.api) + implementation(projects.libraries.architecture) implementation(projects.libraries.compound) implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) diff --git a/features/home/impl/build.gradle.kts b/features/home/impl/build.gradle.kts index b36ee6aed2..0635da39a5 100644 --- a/features/home/impl/build.gradle.kts +++ b/features/home/impl/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(projects.libraries.permissions.noop) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.features.announcement.api) implementation(projects.features.invite.api) implementation(projects.features.networkmonitor.api) diff --git a/features/invite/impl/build.gradle.kts b/features/invite/impl/build.gradle.kts index 80b98464f7..e033f2740c 100644 --- a/features/invite/impl/build.gradle.kts +++ b/features/invite/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.services.analytics.api) diff --git a/features/invite/test/build.gradle.kts b/features/invite/test/build.gradle.kts index 2df267f155..080ed765bb 100644 --- a/features/invite/test/build.gradle.kts +++ b/features/invite/test/build.gradle.kts @@ -16,6 +16,7 @@ android { dependencies { implementation(libs.coroutines.core) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.test) implementation(projects.tests.testutils) diff --git a/features/location/test/build.gradle.kts b/features/location/test/build.gradle.kts index f84e8ba772..e51737d40c 100644 --- a/features/location/test/build.gradle.kts +++ b/features/location/test/build.gradle.kts @@ -16,7 +16,7 @@ android { dependencies { api(projects.features.location.api) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(libs.appyx.core) implementation(projects.tests.testutils) } diff --git a/features/logout/impl/build.gradle.kts b/features/logout/impl/build.gradle.kts index 8de7718980..d5356ced63 100644 --- a/features/logout/impl/build.gradle.kts +++ b/features/logout/impl/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) implementation(projects.libraries.dateformatter.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.workmanager.api) api(projects.features.logout.api) diff --git a/features/messages/test/build.gradle.kts b/features/messages/test/build.gradle.kts index b839f8de06..f89dd8de06 100644 --- a/features/messages/test/build.gradle.kts +++ b/features/messages/test/build.gradle.kts @@ -16,6 +16,7 @@ android { dependencies { api(projects.features.messages.impl) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.test) implementation(projects.libraries.audio.test) implementation(projects.libraries.mediaplayer.test) diff --git a/features/poll/test/build.gradle.kts b/features/poll/test/build.gradle.kts index a3779809d7..d0adc8e94f 100644 --- a/features/poll/test/build.gradle.kts +++ b/features/poll/test/build.gradle.kts @@ -15,6 +15,7 @@ android { } dependencies { + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) api(projects.features.poll.api) implementation(libs.kotlinx.collections.immutable) diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index ad28c90966..889e030213 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.push.api) implementation(projects.libraries.pushproviders.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiUtils) implementation(projects.libraries.fullscreenintent.api) implementation(projects.features.rageshake.api) diff --git a/features/preferences/test/build.gradle.kts b/features/preferences/test/build.gradle.kts index 7e3da4a6e8..a066fe4707 100644 --- a/features/preferences/test/build.gradle.kts +++ b/features/preferences/test/build.gradle.kts @@ -14,6 +14,7 @@ android { } dependencies { + implementation(projects.libraries.architecture) implementation(projects.features.preferences.api) implementation(projects.tests.testutils) } diff --git a/features/signedout/impl/build.gradle.kts b/features/signedout/impl/build.gradle.kts index 3c8aac5e25..b3801288be 100644 --- a/features/signedout/impl/build.gradle.kts +++ b/features/signedout/impl/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiStrings) testCommonDependencies(libs) diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index 1c70006cc8..2a7586d6c9 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -49,9 +49,9 @@ dependencies { implementation(projects.libraries.core) implementation(projects.services.analytics.api) implementation(libs.serialization.json) - api(projects.libraries.sessionStorage.api) + implementation(projects.libraries.sessionStorage.api) implementation(libs.coroutines.core) - api(projects.libraries.architecture) + implementation(projects.libraries.architecture) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 67386cc592..92e8a3ba9e 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -32,10 +32,12 @@ dependencies { implementation(projects.appconfig) implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) implementation(projects.libraries.di) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.network) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.workmanager.api) implementation(projects.services.analytics.api) implementation(projects.services.toolbox.api) diff --git a/libraries/matrix/test/build.gradle.kts b/libraries/matrix/test/build.gradle.kts index 63836d857a..ccfa56f1aa 100644 --- a/libraries/matrix/test/build.gradle.kts +++ b/libraries/matrix/test/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { api(projects.libraries.matrix.api) api(libs.coroutines.core) implementation(libs.coroutines.test) + implementation(projects.libraries.architecture) implementation(projects.services.analytics.api) implementation(projects.tests.testutils) implementation(libs.kotlinx.collections.immutable) diff --git a/libraries/matrixmedia/impl/build.gradle.kts b/libraries/matrixmedia/impl/build.gradle.kts index 82afc2f62c..56ccc79afc 100644 --- a/libraries/matrixmedia/impl/build.gradle.kts +++ b/libraries/matrixmedia/impl/build.gradle.kts @@ -19,8 +19,10 @@ android { setupDependencyInjection() dependencies { + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixmedia.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.designsystem) implementation(libs.coil.compose) implementation(libs.coil.gif) diff --git a/libraries/mediaupload/test/build.gradle.kts b/libraries/mediaupload/test/build.gradle.kts index 7e729089c7..f271ae1316 100644 --- a/libraries/mediaupload/test/build.gradle.kts +++ b/libraries/mediaupload/test/build.gradle.kts @@ -15,6 +15,7 @@ android { } dependencies { + implementation(libs.coroutines.core) api(projects.libraries.mediaupload.api) implementation(projects.libraries.core) implementation(projects.tests.testutils) diff --git a/libraries/mediaviewer/test/build.gradle.kts b/libraries/mediaviewer/test/build.gradle.kts index 1918714d7b..87665e6d69 100644 --- a/libraries/mediaviewer/test/build.gradle.kts +++ b/libraries/mediaviewer/test/build.gradle.kts @@ -18,6 +18,7 @@ android { dependencies { api(projects.libraries.mediaviewer.impl) + implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.tests.testutils) implementation(projects.libraries.matrix.api) diff --git a/libraries/preferences/impl/build.gradle.kts b/libraries/preferences/impl/build.gradle.kts index c567471da4..0478d303ea 100644 --- a/libraries/preferences/impl/build.gradle.kts +++ b/libraries/preferences/impl/build.gradle.kts @@ -25,4 +25,5 @@ dependencies { implementation(projects.libraries.di) implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.sessionStorage.api) } diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index 49ce7135d5..ffa4e9fa70 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) implementation(projects.libraries.push.api) + implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiStrings) implementation(projects.libraries.troubleshoot.api) implementation(projects.services.toolbox.api) diff --git a/libraries/recentemojis/impl/build.gradle.kts b/libraries/recentemojis/impl/build.gradle.kts index a1a72c8672..061a7ecd89 100644 --- a/libraries/recentemojis/impl/build.gradle.kts +++ b/libraries/recentemojis/impl/build.gradle.kts @@ -21,6 +21,7 @@ setupDependencyInjection() dependencies { api(projects.libraries.recentemojis.api) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(libs.kotlinx.collections.immutable) implementation(libs.matrix.emojibase.bindings) diff --git a/libraries/session-storage/test/build.gradle.kts b/libraries/session-storage/test/build.gradle.kts index cfdc3018a9..7a89746812 100644 --- a/libraries/session-storage/test/build.gradle.kts +++ b/libraries/session-storage/test/build.gradle.kts @@ -14,6 +14,7 @@ android { } dependencies { + implementation(libs.coroutines.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.sessionStorage.api) } diff --git a/libraries/voiceplayer/api/build.gradle.kts b/libraries/voiceplayer/api/build.gradle.kts index f37c263d83..e058210b7d 100644 --- a/libraries/voiceplayer/api/build.gradle.kts +++ b/libraries/voiceplayer/api/build.gradle.kts @@ -16,5 +16,6 @@ android { dependencies { implementation(libs.androidx.annotationjvm) implementation(libs.coroutines.core) + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) } diff --git a/libraries/voiceplayer/impl/build.gradle.kts b/libraries/voiceplayer/impl/build.gradle.kts index 4aa00e188b..8fe79fb774 100644 --- a/libraries/voiceplayer/impl/build.gradle.kts +++ b/libraries/voiceplayer/impl/build.gradle.kts @@ -21,6 +21,7 @@ setupDependencyInjection() dependencies { api(projects.libraries.voiceplayer.api) + implementation(projects.libraries.architecture) implementation(projects.libraries.audio.api) implementation(projects.libraries.core) implementation(projects.libraries.di) diff --git a/libraries/workmanager/api/build.gradle.kts b/libraries/workmanager/api/build.gradle.kts index b53ed40394..238dc57664 100644 --- a/libraries/workmanager/api/build.gradle.kts +++ b/libraries/workmanager/api/build.gradle.kts @@ -15,6 +15,6 @@ android { dependencies { api(libs.androidx.workmanager.runtime) - + implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) } diff --git a/libraries/workmanager/impl/build.gradle.kts b/libraries/workmanager/impl/build.gradle.kts index 878edb6fe2..c1874bfa74 100644 --- a/libraries/workmanager/impl/build.gradle.kts +++ b/libraries/workmanager/impl/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.di) + implementation(projects.libraries.sessionStorage.api) testCommonDependencies(libs, false) testImplementation(projects.libraries.sessionStorage.test) diff --git a/services/analyticsproviders/sentry/build.gradle.kts b/services/analyticsproviders/sentry/build.gradle.kts index 02dde35ef4..3350df864b 100644 --- a/services/analyticsproviders/sentry/build.gradle.kts +++ b/services/analyticsproviders/sentry/build.gradle.kts @@ -50,6 +50,8 @@ setupDependencyInjection() dependencies { implementation(libs.sentry) + implementation(libs.coroutines.core) + implementation(libs.androidx.annotationjvm) implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) From 078e942a28ef6fe761172615274e8953bd76da93 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 09:54:54 +0200 Subject: [PATCH 044/471] Cleanup dependencies --- libraries/matrix/api/build.gradle.kts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index 2a7586d6c9..2a326527df 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -44,14 +44,12 @@ android { } dependencies { - implementation(projects.libraries.di) - implementation(projects.libraries.androidutils) - implementation(projects.libraries.core) - implementation(projects.services.analytics.api) - implementation(libs.serialization.json) - implementation(projects.libraries.sessionStorage.api) implementation(libs.coroutines.core) + implementation(libs.serialization.json) + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) + implementation(projects.libraries.sessionStorage.api) + implementation(projects.services.analytics.api) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) From 4a79b272ef9bc33aaad05557f97fc105838eca34 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 30 Apr 2026 10:51:29 +0200 Subject: [PATCH 045/471] Fix ANRs when receiving push notifications (#6696) In Sentry there are some reports of methods called when notifications are fetched that end up having ANRs. This looked weird because everything is asynchronous... but it's still running with a `Main` dispatcher. Using the `Default/computation` one instead should be the right call. --- .../libraries/push/impl/push/DefaultPushHandler.kt | 9 ++++++--- .../libraries/push/impl/push/DefaultPushHandlerTest.kt | 9 +++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index 44cf6edefc..d5c3f04348 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.push.impl.push import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.push.impl.db.PushRequest @@ -35,6 +36,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber private val loggerTag = LoggerTag("PushHandler", LoggerTag.PushLoggerTag) @@ -53,6 +55,7 @@ class DefaultPushHandler( private val workManagerScheduler: WorkManagerScheduler, private val syncPendingNotificationsRequestFactory: SyncPendingNotificationsRequestBuilder.Factory, resultProcessor: NotificationResultProcessor, + private val dispatchers: CoroutineDispatchers, ) : PushHandler { init { resultProcessor.start() @@ -64,7 +67,7 @@ class DefaultPushHandler( * @param pushData the data received in the push. * @param providerInfo the provider info. */ - override suspend fun handle(pushData: PushData, providerInfo: String): Boolean { + override suspend fun handle(pushData: PushData, providerInfo: String): Boolean = withContext(dispatchers.computation) { // Start measuring how long it takes to display a notification from when the push is received Timber.d("Calculating push-to-notification for event ${pushData.eventId}") val parent = analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.PushToNotification(pushData.eventId.value)) @@ -81,7 +84,7 @@ class DefaultPushHandler( } // Diagnostic Push - return if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) { + if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) { pushHistoryService.onDiagnosticPush(providerInfo) diagnosticPushHandler.handlePush() false @@ -90,7 +93,7 @@ class DefaultPushHandler( } } - override suspend fun handleInvalid(providerInfo: String, data: String) { + override suspend fun handleInvalid(providerInfo: String, data: String) = withContext(dispatchers.computation) { incrementPushDataStore.incrementPushCounter() pushHistoryService.onInvalidPushReceived(providerInfo, data) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt index a16568d400..f0dee4446c 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandlerTest.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.push.impl.push import app.cash.turbine.test +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -40,7 +41,9 @@ import io.element.android.services.toolbox.test.systemclock.FakeSystemClock 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.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -212,7 +215,7 @@ class DefaultPushHandlerTest { .isCalledOnce() } - private fun createDefaultPushHandler( + private fun TestScope.createDefaultPushHandler( incrementPushCounterResult: () -> Unit = { lambdaError() }, userPushStore: FakeUserPushStore = FakeUserPushStore(), pushClientSecret: PushClientSecret = FakePushClientSecret(), @@ -227,6 +230,7 @@ class DefaultPushHandlerTest { start = {}, stop = {}, ), + dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), ): DefaultPushHandler { return DefaultPushHandler( incrementPushDataStore = object : IncrementPushDataStore { @@ -246,7 +250,8 @@ class DefaultPushHandlerTest { resultProcessor = resultProcessor, syncPendingNotificationsRequestFactory = SyncPendingNotificationsRequestBuilder.Factory { FakeSyncPendingNotificationsRequestBuilder() - } + }, + dispatchers = dispatchers, ) } } From fabdcee520ea2714e6f8ab825e70762676b7801c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 11:19:10 +0200 Subject: [PATCH 046/471] Remove SignInWithClassic FeatureFlag to enable the feature. Closes #6669 --- features/login/impl/build.gradle.kts | 2 -- .../impl/classic/ElementClassicConnection.kt | 12 --------- .../DefaultElementClassicConnectionTest.kt | 25 ------------------- .../libraries/featureflag/api/FeatureFlags.kt | 7 ------ 4 files changed, 46 deletions(-) diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index e739beb20a..86a8e0e7bc 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -60,7 +60,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) @@ -81,7 +80,6 @@ dependencies { testImplementation(projects.features.login.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.preferences.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/classic/ElementClassicConnection.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/classic/ElementClassicConnection.kt index c928c05239..dfddd1d496 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/classic/ElementClassicConnection.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/classic/ElementClassicConnection.kt @@ -28,8 +28,6 @@ import io.element.android.libraries.androidutils.service.ServiceBinder import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.auth.ElementClassicSession import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -71,7 +69,6 @@ class DefaultElementClassicConnection( private val coroutineScope: CoroutineScope, private val matrixAuthenticationService: MatrixAuthenticationService, private val homeServerLoginCompatibilityChecker: HomeServerLoginCompatibilityChecker, - private val featureFlagService: FeatureFlagService, ) : ElementClassicConnection { // Messenger for communicating with the service. private var messenger: Messenger? = null @@ -119,10 +116,6 @@ class DefaultElementClassicConnection( override fun start() { Timber.tag(loggerTag.value).d("start()") coroutineScope.launch { - if (!featureFlagService.isFeatureEnabled(FeatureFlags.SignInWithClassic)) { - Timber.tag(loggerTag.value).d("Login with Element Classic is disabled, not starting connection") - return@launch - } // Establish a connection with the service. We use an explicit // class name because there is no reason to be able to let other // applications replace our component. @@ -158,11 +151,6 @@ class DefaultElementClassicConnection( override fun requestSession() { Timber.tag(loggerTag.value).d("requestSession()") coroutineScope.launch { - if (!featureFlagService.isFeatureEnabled(FeatureFlags.SignInWithClassic)) { - Timber.tag(loggerTag.value).d("Login with Element Classic is disabled") - emitState(ElementClassicConnectionState.Error("The feature is disabled")) - return@launch - } val finalMessenger = messenger if (finalMessenger == null) { Timber.tag(loggerTag.value).d("The messenger is null, can't request data") diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/classic/DefaultElementClassicConnectionTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/classic/DefaultElementClassicConnectionTest.kt index 5da3c97f3c..8ea1b2e3d3 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/classic/DefaultElementClassicConnectionTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/classic/DefaultElementClassicConnectionTest.kt @@ -15,9 +15,6 @@ import androidx.core.graphics.createBitmap import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.androidutils.service.ServiceBinder -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.auth.ElementClassicSession import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -112,21 +109,6 @@ class DefaultElementClassicConnectionTest { } } - @Test - fun `requestSession when the feature is disabled emits an error`() = runTest { - val connection = createDefaultElementClassicConnection( - matrixAuthenticationService = FakeMatrixAuthenticationService( - setElementClassicSessionResult = {}, - ), - isFeatureEnabled = false, - ) - connection.stateFlow.test { - assertThat(awaitItem()).isEqualTo(ElementClassicConnectionState.Idle) - connection.requestSession() - assertThat(awaitItem()).isInstanceOf(ElementClassicConnectionState.Error::class.java) - } - } - @Test fun `when an error is received, an error is emitted`() = runTest { val connection = createDefaultElementClassicConnection( @@ -514,17 +496,10 @@ class DefaultElementClassicConnectionTest { homeServerLoginCompatibilityChecker: HomeServerLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker( checkResult = { Result.success(true) } ), - isFeatureEnabled: Boolean = true, - featureFlagService: FeatureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.SignInWithClassic.key to isFeatureEnabled, - ) - ), ) = DefaultElementClassicConnection( serviceBinder = serviceBinder, coroutineScope = coroutineScope, matrixAuthenticationService = matrixAuthenticationService, homeServerLoginCompatibilityChecker = homeServerLoginCompatibilityChecker, - featureFlagService = featureFlagService, ) } 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 15e61f4260..2378a25fc5 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 @@ -100,13 +100,6 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), - SignInWithClassic( - key = "feature.signin_with_classic", - title = "Sign in with Element Classic", - description = "Allow the application to sign in to the current Element Classic account.", - defaultValue = { false }, - isFinished = false, - ), AllowBlackTheme( key = "feature.allow_black_theme", title = "Allow black theme", From c9bc8791e95a10c8e64d91fa25040b6034760aa9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:19:59 +0000 Subject: [PATCH 047/471] Update kotlin --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b942901e19..c42858bc64 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,9 +5,9 @@ # Project android_gradle_plugin = "8.13.2" # When updating this, please also update the version in the file ./idea/kotlinc.xml -kotlin = "2.3.20" +kotlin = "2.3.21" kotlinpoet = "2.3.0" -ksp = "2.3.6" +ksp = "2.3.7" firebaseAppDistribution = "5.2.1" # AndroidX From 795299ed0ccab9ad629f1f9d62778a066342ab90 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 30 Apr 2026 09:20:58 +0000 Subject: [PATCH 048/471] Update screenshots --- ...imeline.components_TimelineItemCallNotifyView_Day_0_en.png | 4 ++-- ...eline.components_TimelineItemCallNotifyView_Night_0_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 afec980e10..13b76dc106 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:3624fe8448ae4af2481e2023a978ffab2d69f2784b7cef41e5ae2e2dbe8fdbd5 -size 17804 +oid sha256:0a618360cff745818c0ad066b4c6599ff804d9c022e6b54dfec97aa152a84f29 +size 27716 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 8f7f14e64a..4d1d5d8f00 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:6bd02d39619efbcaa6d96f1e75a0d14a572c43e60bad0c3f84d6a5a48b6fbda1 -size 17395 +oid sha256:54e56f77c535db49bb9352ca09033fc81ba21b71e7c28079aec8f9bc09dcc1ad +size 26644 From f5737e9d2b2fb9e202c68533c22fb5ef588eddef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 15:44:02 +0200 Subject: [PATCH 049/471] a11y: add alternative text to the info icon. Closes #6379 --- .../io/element/android/libraries/textcomposer/TextComposer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4860a53ae7..3993b3d64c 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 @@ -679,7 +679,7 @@ private fun TextInputBox( .align(Alignment.CenterEnd), imageVector = CompoundIcons.InfoSolid(), tint = ElementTheme.colors.iconCriticalPrimary, - contentDescription = null, + contentDescription = stringResource(CommonStrings.a11y_info), ) if (showBottomSheet) { CaptionWarningBottomSheet( From b815aaabc9b550192a80fad97f432dcecb954121 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 15:54:07 +0200 Subject: [PATCH 050/471] a11y: let section header be implemented as a heading. Closes #6386 --- .../designsystem/theme/components/ListSectionHeader.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt index 403ed6da97..3a86d72e7d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ListSectionHeader.kt @@ -17,6 +17,8 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -48,6 +50,9 @@ fun ListSectionHeader( verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( + modifier = Modifier.semantics { + heading() + }, text = title, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary, From 0a99b28963ed247da62b2bbd0b54081b48ca8771 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 16:10:19 +0200 Subject: [PATCH 051/471] a11y: let banner title be implemented as a heading. Closes #6384 --- .../libraries/designsystem/components/Announcement.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Announcement.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Announcement.kt index dcd3f8fa21..037c37e3a8 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Announcement.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/Announcement.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons @@ -148,7 +150,11 @@ private fun TitleAndDescription( text = title, style = ElementTheme.typography.fontBodyLgMedium, color = titleColor, - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .semantics { + heading() + }, ) if (trailingContent != null) { Spacer(Modifier.width(12.dp)) From 0c333235f808113c9a37d6d1aacb3b97a0874ff4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 16:16:15 +0200 Subject: [PATCH 052/471] a11y: set role = button for "learn more" texts. Closes #6405 --- .../choosemode/ChooseSelfVerificationModeView.kt | 9 ++++++++- .../impl/outgoing/OutgoingVerificationView.kt | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt index 0ca25c9455..1bfa10daf2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt @@ -19,6 +19,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -90,7 +93,11 @@ fun ChooseSelfVerificationModeView( Text( modifier = Modifier .clickable(onClick = onLearnMore) - .padding(vertical = 4.dp, horizontal = 16.dp), + .padding(vertical = 4.dp, horizontal = 16.dp) + .semantics { + // Note: there is no Role.Link, so we use Role.Button for better accessibility support + role = Role.Button + }, text = stringResource(CommonStrings.action_learn_more), style = ElementTheme.typography.fontBodyLgMedium ) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt index 2dd2850174..1c199f8826 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationView.kt @@ -24,8 +24,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.focused +import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -227,7 +229,11 @@ private fun ContentInitial( Text( modifier = Modifier .clickable { onLearnMoreClick() } - .padding(vertical = 4.dp, horizontal = 16.dp), + .padding(vertical = 4.dp, horizontal = 16.dp) + .semantics { + // Note: there is no Role.Link, so we use Role.Button for better accessibility support + role = Role.Button + }, text = stringResource(CommonStrings.action_learn_more), style = ElementTheme.typography.fontBodyLgMedium ) From 9912d763d41719f57fab36d05d5891b170ba01b6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 16:41:32 +0200 Subject: [PATCH 053/471] a11y: use different content description when the red dot is displayed Closes #6395 --- .../features/home/impl/components/HomeTopBar.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index ff0fc00496..5c3075fc75 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -237,6 +237,7 @@ private fun SpaceFilterButton( else -> Unit } } + val isSelected = spaceFiltersState is SpaceFiltersState.Selected IconButton( onClick = ::onClick, @@ -320,7 +321,15 @@ private fun AccountIcon( Avatar( avatarData = avatarData, avatarType = AvatarType.User, - contentDescription = if (isCurrentAccount) stringResource(CommonStrings.common_settings) else null, + contentDescription = if (isCurrentAccount) { + if (showAvatarIndicator) { + stringResource(CommonStrings.a11y_settings_with_required_action) + } else { + stringResource(CommonStrings.common_settings) + } + } else { + null + }, ) if (showAvatarIndicator) { RedIndicatorAtom( From 70583fc3fce11779dc532104cc509e80158bc976 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 17:25:15 +0200 Subject: [PATCH 054/471] Remove unused content parameter. --- .../io/element/android/libraries/architecture/BaseFlowNode.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt index ce89e8a9d9..da849b753e 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/BaseFlowNode.kt @@ -11,7 +11,6 @@ package io.element.android.libraries.architecture import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier @@ -88,11 +87,9 @@ inline fun BaseFlowNode.OverlayView( @Composable inline fun BaseFlowNode.BackstackWithOverlayBox( modifier: Modifier = Modifier, - content: @Composable BoxScope.() -> Unit = {}, ) { Box(modifier = modifier) { BackstackView() OverlayView() - content() } } From 4e46f12a120f987bcf8c3899aec18d496824e3d2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 17:45:53 +0200 Subject: [PATCH 055/471] Remove useless Box --- .../impl/viewer/MediaViewerView.kt | 78 +++++++++---------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index abea2f66d2..7149592a26 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -204,48 +204,42 @@ fun MediaViewerView( } // Top bar AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) { - Box( - modifier = Modifier - .fillMaxSize() - .navigationBarsPadding() - ) { - when (currentData) { - is MediaViewerPageData.MediaViewerData -> { - MediaViewerTopBar( - data = currentData, - canShowInfo = state.canShowInfo, - onBackClick = onBackClick, - onShareClick = { - state.eventSink(MediaViewerEvent.Share(currentData)) - }, - onSaveClick = { - state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) - }, - onInfoClick = { - state.eventSink(MediaViewerEvent.OpenInfo(currentData)) - }, - ) - } - else -> { - TopAppBar( - title = { - if (currentData is MediaViewerPageData.Loading) { - Text( - modifier = Modifier.semantics { - heading() - }, - text = stringResource(id = CommonStrings.common_loading_more), - style = ElementTheme.typography.fontBodyMdMedium, - color = ElementTheme.colors.textPrimary, - ) - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = bgCanvasWithTransparency, - ), - navigationIcon = { BackButton(onClick = onBackClick) }, - ) - } + when (currentData) { + is MediaViewerPageData.MediaViewerData -> { + MediaViewerTopBar( + data = currentData, + canShowInfo = state.canShowInfo, + onBackClick = onBackClick, + onShareClick = { + state.eventSink(MediaViewerEvent.Share(currentData)) + }, + onSaveClick = { + state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) + }, + onInfoClick = { + state.eventSink(MediaViewerEvent.OpenInfo(currentData)) + }, + ) + } + else -> { + TopAppBar( + title = { + if (currentData is MediaViewerPageData.Loading) { + Text( + modifier = Modifier.semantics { + heading() + }, + text = stringResource(id = CommonStrings.common_loading_more), + style = ElementTheme.typography.fontBodyMdMedium, + color = ElementTheme.colors.textPrimary, + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = bgCanvasWithTransparency, + ), + navigationIcon = { BackButton(onClick = onBackClick) }, + ) } } } From dcd0a98c0c73c2d23a0be25ec5763d5f84b73893 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Apr 2026 17:49:31 +0200 Subject: [PATCH 056/471] Declare Top bar first and use zIndex. --- .../impl/viewer/MediaViewerView.kt | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 7149592a26..f22f326986 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -59,6 +59,7 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import coil3.compose.AsyncImage import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons @@ -130,6 +131,52 @@ fun MediaViewerView( state.eventSink(MediaViewerEvent.OnNavigateTo(page)) } } + // Top bar + AnimatedVisibility( + modifier = Modifier.zIndex(1f), + visible = showOverlay, + enter = fadeIn(), + exit = fadeOut(), + ) { + when (currentData) { + is MediaViewerPageData.MediaViewerData -> { + MediaViewerTopBar( + data = currentData, + canShowInfo = state.canShowInfo, + onBackClick = onBackClick, + onShareClick = { + state.eventSink(MediaViewerEvent.Share(currentData)) + }, + onSaveClick = { + state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) + }, + onInfoClick = { + state.eventSink(MediaViewerEvent.OpenInfo(currentData)) + }, + ) + } + else -> { + TopAppBar( + title = { + if (currentData is MediaViewerPageData.Loading) { + Text( + modifier = Modifier.semantics { + heading() + }, + text = stringResource(id = CommonStrings.common_loading_more), + style = ElementTheme.typography.fontBodyMdMedium, + color = ElementTheme.colors.textPrimary, + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = bgCanvasWithTransparency, + ), + navigationIcon = { BackButton(onClick = onBackClick) }, + ) + } + } + } HorizontalPager( state = pagerState, modifier = Modifier, @@ -202,47 +249,6 @@ fun MediaViewerView( } } } - // Top bar - AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) { - when (currentData) { - is MediaViewerPageData.MediaViewerData -> { - MediaViewerTopBar( - data = currentData, - canShowInfo = state.canShowInfo, - onBackClick = onBackClick, - onShareClick = { - state.eventSink(MediaViewerEvent.Share(currentData)) - }, - onSaveClick = { - state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) - }, - onInfoClick = { - state.eventSink(MediaViewerEvent.OpenInfo(currentData)) - }, - ) - } - else -> { - TopAppBar( - title = { - if (currentData is MediaViewerPageData.Loading) { - Text( - modifier = Modifier.semantics { - heading() - }, - text = stringResource(id = CommonStrings.common_loading_more), - style = ElementTheme.typography.fontBodyMdMedium, - color = ElementTheme.colors.textPrimary, - ) - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = bgCanvasWithTransparency, - ), - navigationIcon = { BackButton(onClick = onBackClick) }, - ) - } - } - } } when (val bottomSheetState = state.mediaBottomSheetState) { From 5e963fc743b3a279db90006cb63ec2fe853f2176 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 10:08:37 +0200 Subject: [PATCH 057/471] a11y: do not use Overlay if screen reader is active, or external keyboard is connected. Related to #6399 --- .../messages/impl/MessagesFlowNode.kt | 29 +++++++++-- .../features/messages/impl/MessagesNode.kt | 9 ++-- .../pinned/list/PinnedMessagesListNode.kt | 9 +++- .../impl/threads/ThreadedMessagesNode.kt | 9 ++-- .../ui/utils/a11y/hasExternalKeyboard.kt | 49 +++++++++++++++++++ 5 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 36e94ec456..4079a540f6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -143,6 +143,7 @@ class MessagesFlowNode( val mediaInfo: MediaInfo, val mediaSource: MediaSource, val thumbnailSource: MediaSource?, + val canUseOverlay: Boolean, ) : NavTarget @Parcelize @@ -227,10 +228,11 @@ class MessagesFlowNode( callback.navigateToRoomDetails() } - override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { + override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean { return processEventClick( timelineMode = timelineMode, event = event, + canUseOverlay = canUseOverlay, ) } @@ -320,7 +322,11 @@ class MessagesFlowNode( ) val callback = object : MediaViewerEntryPoint.Callback { override fun onDone() { - overlay.hide() + if (navTarget.canUseOverlay) { + overlay.hide() + } else { + backstack.pop() + } } override fun viewInTimeline(eventId: EventId) { @@ -414,10 +420,11 @@ class MessagesFlowNode( } NavTarget.PinnedMessagesList -> { val callback = object : PinnedMessagesListNode.Callback { - override fun handleEventClick(event: TimelineItem.Event) { + override fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean) { processEventClick( timelineMode = Timeline.Mode.PinnedEvents, event = event, + canUseOverlay = canUseOverlay, ) } @@ -456,10 +463,11 @@ class MessagesFlowNode( focusedEventId = navTarget.focusedEventId, ) val callback = object : ThreadedMessagesNode.Callback { - override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { + override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean { return processEventClick( timelineMode = timelineMode, event = event, + canUseOverlay = canUseOverlay, ) } @@ -547,6 +555,7 @@ class MessagesFlowNode( private fun processEventClick( timelineMode: Timeline.Mode, event: TimelineItem.Event, + canUseOverlay: Boolean, ): Boolean { val navTarget = when (event.content) { is TimelineItemImageContent -> { @@ -556,6 +565,7 @@ class MessagesFlowNode( content = event.content, mediaSource = event.content.mediaSource, thumbnailSource = event.content.thumbnailSource, + canUseOverlay = canUseOverlay, ) } is TimelineItemVideoContent -> { @@ -565,6 +575,7 @@ class MessagesFlowNode( content = event.content, mediaSource = event.content.mediaSource, thumbnailSource = event.content.thumbnailSource, + canUseOverlay = canUseOverlay, ) } is TimelineItemFileContent -> { @@ -574,6 +585,7 @@ class MessagesFlowNode( content = event.content, mediaSource = event.content.mediaSource, thumbnailSource = event.content.thumbnailSource, + canUseOverlay = canUseOverlay, ) } is TimelineItemAudioContent -> { @@ -583,6 +595,7 @@ class MessagesFlowNode( content = event.content, mediaSource = event.content.mediaSource, thumbnailSource = null, + canUseOverlay = canUseOverlay, ) } is TimelineItemLocationContent -> { @@ -603,7 +616,11 @@ class MessagesFlowNode( } return when (navTarget) { is NavTarget.MediaViewer -> { - overlay.show(navTarget) + if (canUseOverlay) { + overlay.show(navTarget) + } else { + backstack.push(navTarget) + } true } is NavTarget.LocationViewer -> { @@ -620,6 +637,7 @@ class MessagesFlowNode( content: TimelineItemEventContentWithAttachment, mediaSource: MediaSource, thumbnailSource: MediaSource?, + canUseOverlay: Boolean, ): NavTarget { return NavTarget.MediaViewer( mode = mode, @@ -647,6 +665,7 @@ class MessagesFlowNode( ), mediaSource = mediaSource, thumbnailSource = thumbnailSource, + canUseOverlay = canUseOverlay, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index a2cf4a3da0..f8c54d284e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -68,6 +68,8 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard +import io.element.android.libraries.ui.utils.time.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.finishLongRunningTransaction @@ -115,7 +117,7 @@ class MessagesNode( ) interface Callback : Plugin { - fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean + fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) fun navigateToRoomMemberDetails(userId: UserId) fun handlePermalinkClick(data: PermalinkData) @@ -247,6 +249,7 @@ class MessagesNode( override fun View(modifier: Modifier) { val activity = requireNotNull(LocalActivity.current) val isDark = ElementTheme.isLightTheme.not() + val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard() CompositionLocalProvider( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { @@ -268,11 +271,11 @@ class MessagesNode( onRoomDetailsClick = callback::navigateToRoomDetails, onEventContentClick = { isLive, event -> if (isLive) { - callback.handleEventClick(timelineController.mainTimelineMode(), event) + callback.handleEventClick(timelineController.mainTimelineMode(), event, canUseOverlay) } else { val detachedTimelineMode = timelineController.detachedTimelineMode() if (detachedTimelineMode != null) { - callback.handleEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event, canUseOverlay) } else { false } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index cddc1831db..9800f0296a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -38,6 +38,8 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard +import io.element.android.libraries.ui.utils.time.isTalkbackActive @ContributesNode(RoomScope::class) @AssistedInject @@ -50,7 +52,7 @@ class PinnedMessagesListNode( private val permalinkParser: PermalinkParser, ) : Node(buildContext, plugins = plugins), PinnedMessagesListNavigator { interface Callback : Plugin { - fun handleEventClick(event: TimelineItem.Event) + fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean) fun navigateToRoomMemberDetails(userId: UserId) fun viewInTimeline(eventId: EventId) fun handlePermalinkClick(data: PermalinkData.RoomLink) @@ -103,6 +105,7 @@ class PinnedMessagesListNode( @Composable override fun View(modifier: Modifier) { + val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard() CompositionLocalProvider( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { @@ -113,7 +116,9 @@ class PinnedMessagesListNode( PinnedMessagesListView( state = state, onBackClick = ::navigateUp, - onEventClick = callback::handleEventClick, + onEventClick = { + callback.handleEventClick(it, canUseOverlay) + }, onUserDataClick = { callback.navigateToRoomMemberDetails(it.userId) }, onLinkClick = { link -> onLinkClick(context, link.url) }, onLinkLongClick = { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 0949237862..c51c24c168 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -68,6 +68,8 @@ import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer +import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard +import io.element.android.libraries.ui.utils.time.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.collections.immutable.ImmutableList @@ -124,7 +126,7 @@ class ThreadedMessagesNode( } interface Callback : Plugin { - fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean + fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) fun navigateToRoomMemberDetails(userId: UserId) fun handlePermalinkClick(data: PermalinkData) @@ -252,6 +254,7 @@ class ThreadedMessagesNode( override fun View(modifier: Modifier) { val activity = requireNotNull(LocalActivity.current) val isDark = ElementTheme.isLightTheme.not() + val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard() CompositionLocalProvider( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { @@ -271,11 +274,11 @@ class ThreadedMessagesNode( onEventContentClick = { isLive, event -> timelineController?.let { controller -> if (isLive) { - callback.handleEventClick(controller.mainTimelineMode(), event) + callback.handleEventClick(controller.mainTimelineMode(), event, canUseOverlay) } else { val detachedTimelineMode = controller.detachedTimelineMode() if (detachedTimelineMode != null) { - callback.handleEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event, canUseOverlay) } else { false } diff --git a/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt b/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt new file mode 100644 index 0000000000..ed35cfbc3e --- /dev/null +++ b/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.ui.utils.a11y + +import android.app.Activity +import android.app.Application +import android.content.res.Configuration +import android.os.Build +import android.os.Bundle +import androidx.activity.compose.LocalActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue + +@Composable +fun hasExternalKeyboard(): Boolean { + val activity = requireNotNull(LocalActivity.current) + var hasExternalKeyboard by remember { mutableStateOf(activity.resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS) } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + DisposableEffect(Unit) { + val callback = object : Application.ActivityLifecycleCallbacks { + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + override fun onActivityStarted(activity: Activity) {} + override fun onActivityResumed(activity: Activity) { + // We do not have access to onActivityConfigurationChanged, so update the value when tha Activity is resumed + hasExternalKeyboard = activity.resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS + } + + override fun onActivityPaused(activity: Activity) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityDestroyed(activity: Activity) {} + } + activity.registerActivityLifecycleCallbacks(callback) + onDispose { + activity.unregisterActivityLifecycleCallbacks(callback) + } + } + } + return hasExternalKeyboard +} From 97cada743278217b137c960d9b25241564c5a6b1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 10:09:50 +0200 Subject: [PATCH 058/471] Move isTalkbackActive to a11y package. --- .../io/element/android/features/messages/impl/MessagesNode.kt | 2 +- .../messages/impl/pinned/list/PinnedMessagesListNode.kt | 2 +- .../features/messages/impl/threads/ThreadedMessagesNode.kt | 2 +- .../android/features/messages/impl/timeline/TimelineView.kt | 2 +- .../messages/impl/timeline/components/MessageEventBubble.kt | 2 +- .../messages/impl/timeline/components/TimelineItemEventRow.kt | 2 +- .../impl/timeline/components/TimelineItemGroupedEventsRow.kt | 2 +- .../messages/impl/timeline/components/TimelineItemRow.kt | 2 +- .../impl/timeline/components/event/TimelineItemImageView.kt | 2 +- .../impl/timeline/components/event/TimelineItemVideoView.kt | 2 +- .../impl/timeline/components/event/TimelineItemVoiceView.kt | 2 +- .../libraries/ui/utils/{time => a11y}/IsTalkbackEnabled.kt | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) rename libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/{time => a11y}/IsTalkbackEnabled.kt (96%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index f8c54d284e..308cda506e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -69,7 +69,7 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.finishLongRunningTransaction diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index 9800f0296a..a618117950 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -39,7 +39,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive @ContributesNode(RoomScope::class) @AssistedInject diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index c51c24c168..0c58316b5e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -69,7 +69,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.collections.immutable.ImmutableList diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 2105cf9df7..41a828abb4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -77,7 +77,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.link.Link import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index aa5aaa2075..80bd342d01 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -47,8 +47,8 @@ import io.element.android.libraries.designsystem.theme.messageFromMeBackground import io.element.android.libraries.designsystem.theme.messageFromOtherBackground import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.libraries.ui.utils.graphics.drawInLayer -import io.element.android.libraries.ui.utils.time.isTalkbackActive private val BUBBLE_RADIUS = 12.dp private val avatarRadius = AvatarSize.TimelineSender.dp / 2 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 976fa3c17e..f15d4d39a5 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 @@ -120,7 +120,7 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.link.Link import kotlinx.coroutines.launch import kotlin.math.abs diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 8316911843..df2b9d8691 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -34,7 +34,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.link.Link @Composable 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 e75df2f89f..842b7a08f5 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 @@ -47,7 +47,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.link.Link import kotlin.time.DurationUnit 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 a8cbb89e96..bef06bcd73 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 @@ -54,7 +54,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.compose.EditorStyledText import io.element.android.wysiwyg.link.Link 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 f5e760736e..8d1ef18f39 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 @@ -64,7 +64,7 @@ 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 -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.compose.EditorStyledText import io.element.android.wysiwyg.link.Link diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index 5dbd0c478f..86e3f1c849 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -52,7 +52,7 @@ 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 -import io.element.android.libraries.ui.utils.time.isTalkbackActive +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.libraries.voiceplayer.api.VoiceMessageEvent import io.element.android.libraries.voiceplayer.api.VoiceMessageState import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider diff --git a/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/time/IsTalkbackEnabled.kt b/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/IsTalkbackEnabled.kt similarity index 96% rename from libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/time/IsTalkbackEnabled.kt rename to libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/IsTalkbackEnabled.kt index 60ac1887c6..938a774355 100644 --- a/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/time/IsTalkbackEnabled.kt +++ b/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/IsTalkbackEnabled.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.ui.utils.time +package io.element.android.libraries.ui.utils.a11y import android.view.accessibility.AccessibilityManager import androidx.compose.runtime.Composable From 562e36b5eae3cae43344ea88c1d93c3bb9c21015 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 10:41:36 +0200 Subject: [PATCH 059/471] Improve how the ThumbnailView is added to the composition --- .../impl/viewer/MediaViewerView.kt | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index f22f326986..f767fdbde1 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -373,11 +373,12 @@ private fun MediaViewerPage( isUserSelected = isUserSelected, audioFocus = audioFocus, ) - ThumbnailView( - mediaInfo = data.mediaInfo, - thumbnailSource = data.thumbnailSource, - isVisible = showThumbnail, - ) + if (showThumbnail) { + ThumbnailView( + mediaInfo = data.mediaInfo, + thumbnailSource = data.thumbnailSource, + ) + } if (showError) { ErrorView( errorMessage = stringResource(id = CommonStrings.error_unknown), @@ -603,7 +604,6 @@ private val maxCaptionHeightLandscape = 128.dp @Composable private fun ThumbnailView( thumbnailSource: MediaSource?, - isVisible: Boolean, mediaInfo: MediaInfo, modifier: Modifier = Modifier, ) { @@ -611,21 +611,19 @@ private fun ThumbnailView( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - if (isVisible) { - val mediaRequestData = MediaRequestData( - source = thumbnailSource, - kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType) - ) - val alpha = if (LocalInspectionMode.current) 0.1f else 1f - AsyncImage( - modifier = Modifier - .fillMaxSize() - .alpha(alpha), - model = mediaRequestData, - contentScale = ContentScale.Fit, - contentDescription = null, - ) - } + val mediaRequestData = MediaRequestData( + source = thumbnailSource, + kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType) + ) + val alpha = if (LocalInspectionMode.current) 0.1f else 1f + AsyncImage( + modifier = Modifier + .fillMaxSize() + .alpha(alpha), + model = mediaRequestData, + contentScale = ContentScale.Fit, + contentDescription = null, + ) } } From 8188ef1463701911daf441f8b498441abe87ede7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 11:31:32 +0200 Subject: [PATCH 060/471] Improve MediaViewerBottomBar usage. --- .../impl/viewer/MediaViewerView.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index f767fdbde1..c3c8aa8907 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -233,17 +233,17 @@ fun MediaViewerView( isUserSelected = (state.listData[page] as? MediaViewerPageData.MediaViewerData)?.eventId == state.initiallySelectedEventId, ) // Bottom bar - AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) { - Box( - modifier = Modifier.fillMaxSize() - ) { - MediaViewerBottomBar( - modifier = Modifier.align(Alignment.BottomCenter), - showDivider = dataForPage.mediaInfo.mimeType.isMimeTypeVideo(), - caption = dataForPage.mediaInfo.caption, - onHeightChange = { bottomPaddingInPixels = it }, - ) - } + AnimatedVisibility( + visible = showOverlay, + enter = fadeIn(), + exit = fadeOut(), + modifier = Modifier.align(Alignment.BottomCenter), + ) { + MediaViewerBottomBar( + showDivider = dataForPage.mediaInfo.mimeType.isMimeTypeVideo(), + caption = dataForPage.mediaInfo.caption, + onHeightChange = { bottomPaddingInPixels = it }, + ) } } } From b1890de26aa36097ea01eaeb1a1d819c2a5e0608 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 11:46:57 +0200 Subject: [PATCH 061/471] MediaViewerView: move TopBar to Scaffold topbar --- .../impl/viewer/MediaViewerView.kt | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index c3c8aa8907..5b10fbfe96 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -121,6 +121,52 @@ fun MediaViewerView( Scaffold( modifier, containerColor = Color.Transparent, + topBar = { + AnimatedVisibility( + visible = showOverlay, + enter = fadeIn(), + exit = fadeOut(), + ) { + when (currentData) { + is MediaViewerPageData.MediaViewerData -> { + MediaViewerTopBar( + data = currentData, + canShowInfo = state.canShowInfo, + onBackClick = onBackClick, + onShareClick = { + state.eventSink(MediaViewerEvent.Share(currentData)) + }, + onSaveClick = { + state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) + }, + onInfoClick = { + state.eventSink(MediaViewerEvent.OpenInfo(currentData)) + }, + ) + } + else -> { + TopAppBar( + title = { + if (currentData is MediaViewerPageData.Loading) { + Text( + modifier = Modifier.semantics { + heading() + }, + text = stringResource(id = CommonStrings.common_loading_more), + style = ElementTheme.typography.fontBodyMdMedium, + color = ElementTheme.colors.textPrimary, + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = bgCanvasWithTransparency, + ), + navigationIcon = { BackButton(onClick = onBackClick) }, + ) + } + } + } + }, snackbarHost = { SnackbarHost(snackbarHostState) }, ) { val pagerState = rememberPagerState(state.currentIndex, 0f) { @@ -131,52 +177,6 @@ fun MediaViewerView( state.eventSink(MediaViewerEvent.OnNavigateTo(page)) } } - // Top bar - AnimatedVisibility( - modifier = Modifier.zIndex(1f), - visible = showOverlay, - enter = fadeIn(), - exit = fadeOut(), - ) { - when (currentData) { - is MediaViewerPageData.MediaViewerData -> { - MediaViewerTopBar( - data = currentData, - canShowInfo = state.canShowInfo, - onBackClick = onBackClick, - onShareClick = { - state.eventSink(MediaViewerEvent.Share(currentData)) - }, - onSaveClick = { - state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) - }, - onInfoClick = { - state.eventSink(MediaViewerEvent.OpenInfo(currentData)) - }, - ) - } - else -> { - TopAppBar( - title = { - if (currentData is MediaViewerPageData.Loading) { - Text( - modifier = Modifier.semantics { - heading() - }, - text = stringResource(id = CommonStrings.common_loading_more), - style = ElementTheme.typography.fontBodyMdMedium, - color = ElementTheme.colors.textPrimary, - ) - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = bgCanvasWithTransparency, - ), - navigationIcon = { BackButton(onClick = onBackClick) }, - ) - } - } - } HorizontalPager( state = pagerState, modifier = Modifier, From d6f8c13c3f1c9b0480c28778c0dccd86ef6ce0a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Apr 2026 12:08:19 +0200 Subject: [PATCH 062/471] MediaPlayerControllerView: Use IconButton instead of Box and remove the clipping. --- .../local/player/MediaPlayerControllerView.kt | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index b83c598c10..b06b97f491 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -12,13 +12,12 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.IconButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -28,7 +27,6 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter @@ -91,33 +89,31 @@ fun MediaPlayerControllerView( .widthIn(max = 480.dp), verticalAlignment = Alignment.CenterVertically, ) { - val bgColor = if (state.isPlaying) { - ElementTheme.colors.bgCanvasDefault + val colors = if (state.isPlaying) { + IconButtonDefaults.iconButtonColors( + containerColor = ElementTheme.colors.bgCanvasDefault, + contentColor = ElementTheme.colors.iconPrimary, + ) } else { - ElementTheme.colors.textPrimary + IconButtonDefaults.iconButtonColors( + containerColor = ElementTheme.colors.iconPrimary, + contentColor = ElementTheme.colors.iconOnSolidPrimary, + ) } - Box( + IconButton( modifier = Modifier - .size(36.dp) - .background( - color = bgColor, - shape = CircleShape, - ) - .clip(CircleShape) - .clickable { onTogglePlay() } - .padding(8.dp), - contentAlignment = Alignment.Center, + .size(36.dp), + onClick = onTogglePlay, + colors = colors, ) { if (state.isPlaying) { Icon( imageVector = CompoundIcons.PauseSolid(), - tint = ElementTheme.colors.iconPrimary, contentDescription = stringResource(CommonStrings.a11y_pause) ) } else { Icon( imageVector = CompoundIcons.PlaySolid(), - tint = ElementTheme.colors.iconOnSolidPrimary, contentDescription = stringResource(CommonStrings.a11y_play) ) } From 0b04dec85c88d76c412e1f53b4a9c3033e05ff88 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 30 Apr 2026 10:44:04 +0000 Subject: [PATCH 063/471] Update screenshots --- .../images/features.roomdetails.impl_RoomDetailsA11y_en.png | 4 ++-- ...s.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png | 4 ++-- ...s.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en.png | 4 ++-- ...mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png | 4 ++-- ...mediaviewer.impl.local.audio_MediaAudioView_Night_1_en.png | 4 ++-- ...r.impl.local.player_MediaPlayerControllerView_Day_0_en.png | 4 ++-- ...r.impl.local.player_MediaPlayerControllerView_Day_1_en.png | 4 ++-- ...r.impl.local.player_MediaPlayerControllerView_Day_2_en.png | 4 ++-- ...impl.local.player_MediaPlayerControllerView_Night_0_en.png | 4 ++-- ...impl.local.player_MediaPlayerControllerView_Night_1_en.png | 4 ++-- ...impl.local.player_MediaPlayerControllerView_Night_2_en.png | 4 ++-- ...s.mediaviewer.impl.local.video_MediaVideoView_Day_0_en.png | 4 ++-- ...mediaviewer.impl.local.video_MediaVideoView_Night_0_en.png | 4 ++-- ...mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png | 4 ++-- ...ibraries.mediaviewer.impl.viewer_MediaViewerView_13_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png | 4 ++-- 21 files changed, 42 insertions(+), 42 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png index 8d8186f2d3..5f0cdd8eca 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2119838c9649710465dc1b8610550ce101f3016a1a6511c3f6dadc715fb75862 -size 82975 +oid sha256:33d583fac967f383a3d3535c4ac38aaccdcbf4a1d48323ff375a239dbce81838 +size 83494 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png index 8ca8df6e8e..3c218ccbbc 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c7dea18de5eabe820fe8670cd09d7b68160a97f31e4f1c474c790d829854ef7 -size 25291 +oid sha256:b918ce7162d95c873f0029a657ee07fca2e34926e4c3cdb39eaeb10123a08721 +size 25340 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en.png index e7f7345c31..37ecdb10ac 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98b39ded83aac1bb5befba6749c08a328b6855bcbd491c1a2f669c848ce72c31 -size 22982 +oid sha256:478c8ee2d55bb2a9f99b8d83c6e0eb0b316237ff1a60a6a735b5eb06f7ca083e +size 23029 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png index 086f166d79..e6925f9f5b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f7aa184c28a4281a30a443b208bf3f64d9a0f85ad135ef50c999d40362c67d4 -size 24670 +oid sha256:d62cf7beeba92ee47193a871becad07f50d27e6f6fa0b85f6c620c3f1b04b6ce +size 24697 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en.png index 86ab39550c..e62cdf3ec4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e13b61fcb56fbd64064bc35628957cb68507efb7fdc25280d6dbf1e6477addc -size 22637 +oid sha256:d684c9ed8b2c38cdf7017194ca656ca8da161ab0a40ef3270c6e3988d8e7f144 +size 22664 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en.png index 67fbefb086..ad12fe8923 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbd0574dba895e157ff5b69ab23f3b389d280538810b457d2860ae5d77331705 -size 8256 +oid sha256:2b55fce1bf0d6b764aaed8eed0fbc1416c5aa1c5b6160775a641dfa10addb227 +size 8231 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en.png index 6b140844b0..db6b026090 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e87c0990dcaf2511a2a0573a04bf14c6a9a7d24e0291fed18aa3a9ea28da572 -size 7543 +oid sha256:4ca666990cb421602e21be0dbf4ec075e8d3e94f9835464923bd31d2414d9d57 +size 7594 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_2_en.png index de0bd78c7d..e40d0af8b2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ed14962b63afa972c68a53d2366b9f00906bab2f3436220bb28643fcb1d56cb -size 7668 +oid sha256:01726f688e5149a460cb1004dff0c7700302a3fe7179829603625dd369b20e78 +size 7659 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en.png index bded2caa96..65f3240329 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a50093fb47b3b17a2ccd1aeb2a10fb6b5d368add8f7d458e50f0cc8643128b7c -size 8053 +oid sha256:8c30a496415f6c00f5ce3a01c775c78994992efd68b2548ecbb5b5ffac054cc1 +size 8041 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en.png index 7030c0a8c4..039388acdc 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a45336123e1eec5ea4b21c6932c1c11d6024685f736454e585a0fb6e6a85ccb2 -size 7701 +oid sha256:10f58df934f372a3b8c68c8e0c4ef61e5bfef242d65daa8f34f4999249bf9e8d +size 7799 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_2_en.png index 41449d52b8..f9f1b8195d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:174f9f97fbce115d36fc5124d3301bd148444acdf24bdc933b5a3fb0754c883d -size 7545 +oid sha256:6d452b48ef65df5257eb708553d667bee46b9d83b1c29a07119c54e342cf1bc2 +size 7527 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en.png index 9b62306041..12f6ba7c36 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed8342b56d749db4d862aaa82d5d312089494965c2879adf18e8fd0c94f8525f -size 13476 +oid sha256:f10ca2d461e4078e46455e007eca2d1e9c9a20dbb6bc24c681fa5164e5f50efd +size 13531 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en.png index 72bd9cfb6c..6273b591af 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04a62974dd81e0786127501b8778f9d302db38a27c984d1b403884942d43accd -size 13209 +oid sha256:da386980ce6a45e102727715b5d50f219e39dcb40a513abdfaab9fa725b8ea41 +size 13260 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en.png index d2231ed54c..9ca317cf9d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:041669475888f2f0c3b2d34502ae72098547340d8c2422ea2a674d38eb6a6241 -size 207620 +oid sha256:d265f0c3fe5bc7f5a6e6d6cfa022ba16ca770b65eeb9a742ba250ff55c4b066a +size 207625 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png index 8a6ebb2746..d404456119 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c56b22d79924d1f463f01428d5be1c69c8068b94a953160fae59a9f6faa112ad -size 206047 +oid sha256:f6870b9c1a5aad4257aa4bd7b13d0be5bea281778061d71c711495aadfaafdb8 +size 206057 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png index bbea97e0b3..77e9dad534 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e72ef42ad838837ce2427af5677931f5db68f876eccc297894c7a2cdd437cffa -size 210729 +oid sha256:094c1de97c33431390f3ff71d73019d9af9a62ed57ba98c94e53f1f9680851fa +size 210728 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png index 67bec8b0d3..c3caba08ff 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58a323c4f06745a877ea546870fbe69ec927aea50584001b3871cad833fca1d4 -size 211343 +oid sha256:2a17d0f4bc47305b9c8a3866f70cdddb16fcb5ba10c8007c8739a329025e846a +size 211342 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_13_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_13_en.png index d5b81bb469..4734e4e6db 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_13_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55671428c52d37c3f63f8b3410817d4e893e2f4327b31b227bc7b7d9ff84b884 -size 134680 +oid sha256:b593f6827598052cbde0e580dcd43b92fb098f6755779b95a60bb691d9ad5003 +size 134711 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png index 26dcbe4099..c44162b0ff 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7bee65567cced59131d471b5c3795347a2ef52cf64ff74b47f1c983a0b4a36f -size 130718 +oid sha256:c0249f33aca3e50713c87a3826e71985991f0996998132c42a374c6169800023 +size 130728 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png index f2ff11333d..932ddfb632 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3cea16c42488306fe29fd22362a0d37c1eeb0fbb1db48f26ad7ff18f6b196ae -size 137605 +oid sha256:43ab4873fc5fc812bf18af50cbe620c83b273ee70305b5ea06a7aeabdf8dbc93 +size 137637 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png index 62de7a4953..924e24ba8a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58655e1846dbe73c287073ac909ee6c881a0ad800e8852653116fdbfd043b9a0 -size 137881 +oid sha256:33048dafab286cf0e5c84040c034d93a42d69c234edd53132d918a4a1c135ece +size 137908 From b8995e4356a85fdc1bb7438366fb7995b368c1d9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 14:09:07 +0200 Subject: [PATCH 064/471] Fix quality issues --- .../android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt | 1 - .../a11y/{hasExternalKeyboard.kt => HasExternalKeyboard.kt} | 0 2 files changed, 1 deletion(-) rename libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/{hasExternalKeyboard.kt => HasExternalKeyboard.kt} (100%) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 5b10fbfe96..738d940453 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -59,7 +59,6 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex import coil3.compose.AsyncImage import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons diff --git a/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt b/libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/HasExternalKeyboard.kt similarity index 100% rename from libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/hasExternalKeyboard.kt rename to libraries/ui-utils/src/main/kotlin/io/element/android/libraries/ui/utils/a11y/HasExternalKeyboard.kt From 13775f4fbdf7af89603785e6072d2f902dd3d401 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 14:09:41 +0200 Subject: [PATCH 065/471] Update kotlin version --- .idea/kotlinc.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 76f6344777..f393d5cdd1 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,7 @@ - From 30fd90abb9450e267ba1f9addd69c9d778b2fbca Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 30 Apr 2026 16:01:24 +0200 Subject: [PATCH 066/471] Mitigate a deadlock when loading room timelines (#6674) This may be happening because we were not destroying focused event timelines used for the media viewer/gallery when necessary, and having several of those back paginating *may* have caused a deadlock in the event cache. --- .../impl/datasource/MediaGalleryDataSource.kt | 13 +++++++++---- .../impl/gallery/MediaGalleryPresenter.kt | 2 +- .../impl/viewer/MediaViewerDataSource.kt | 6 ++++-- .../mediaviewer/impl/viewer/MediaViewerPresenter.kt | 2 +- .../impl/viewer/SingleMediaGalleryDataSource.kt | 3 ++- .../impl/datasource/FakeMediaGalleryDataSource.kt | 3 ++- .../TimelineMediaGalleryDataSourceTest.kt | 12 ++++++------ .../impl/viewer/MediaViewerDataSourceTest.kt | 2 +- .../impl/viewer/SingleMediaGalleryDataSourceTest.kt | 4 ++-- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt index 722e14a790..f5418c76c9 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -27,10 +28,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach +import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean interface MediaGalleryDataSource { - fun start() + fun start(coroutineScope: CoroutineScope) fun groupedMediaItemsFlow(): Flow> fun getLastData(): AsyncData suspend fun loadMore(direction: Timeline.PaginationDirection) @@ -58,7 +60,7 @@ class TimelineMediaGalleryDataSource( private val isStarted = AtomicBoolean(false) @OptIn(ExperimentalCoroutinesApi::class) - override fun start() { + override fun start(coroutineScope: CoroutineScope) { if (!isStarted.compareAndSet(false, true)) { return } @@ -96,9 +98,12 @@ class TimelineMediaGalleryDataSource( groupedMediaItemsFlow.emit(AsyncData.Success(groupedMediaItems)) } .onCompletion { - timeline?.close() + timeline?.let { + Timber.d("Timeline media gallery data source flow completed for room ${room.roomId}, closing timeline") + it.close() + } } - .launchIn(room.roomCoroutineScope) + .launchIn(coroutineScope) } override suspend fun loadMore(direction: Timeline.PaginationDirection) { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index ac9b365099..e7caa12f2e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -78,7 +78,7 @@ class MediaGalleryPresenter( .collectAsState(AsyncData.Uninitialized) LaunchedEffect(Unit) { - mediaGalleryDataSource.start() + mediaGalleryDataSource.start(this) } val permissions by room.permissionsAsState(MediaPermissions.DEFAULT) { perms -> diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index a9fb5d645c..24e48531f0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -35,6 +35,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext @@ -62,11 +63,12 @@ class MediaViewerDataSource( private val localMediaStates: MutableMap>> = mutableMapOf() - fun setup() { - galleryDataSource.start() + fun setup(coroutineScope: CoroutineScope) { + galleryDataSource.start(coroutineScope) } fun dispose() { + Timber.d("Disposing MediaViewerDataSource, closing ${mediaFiles.size} media files") mediaFiles.forEach { it.close() } mediaFiles.clear() localMediaStates.clear() diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index b7631a7039..60f03bb1e0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -88,7 +88,7 @@ class MediaViewerPresenter( var mediaBottomSheetState by remember { mutableStateOf(MediaBottomSheetState.Hidden) } DisposableEffect(Unit) { - dataSource.setup() + dataSource.setup(coroutineScope) onDispose { dataSource.dispose() } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt index f243ac4fd7..7bbf397171 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt @@ -20,12 +20,13 @@ import io.element.android.libraries.mediaviewer.impl.datasource.MediaGalleryData import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems import io.element.android.libraries.mediaviewer.impl.model.MediaItem import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flowOf class SingleMediaGalleryDataSource( private val data: GroupedMediaItems, ) : MediaGalleryDataSource { - override fun start() = Unit + override fun start(coroutineScope: CoroutineScope) = Unit override fun groupedMediaItemsFlow() = flowOf(AsyncData.Success(data)) override fun getLastData(): AsyncData = AsyncData.Success(data) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt index c612bba1bc..4a64f02a33 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems import io.element.android.tests.testutils.lambda.lambdaError +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -21,7 +22,7 @@ class FakeMediaGalleryDataSource( private val loadMoreLambda: (Timeline.PaginationDirection) -> Unit = { lambdaError() }, private val deleteItemLambda: (EventId) -> Unit = { lambdaError() }, ) : MediaGalleryDataSource { - override fun start() = startLambda() + override fun start(coroutineScope: CoroutineScope) = startLambda() private val groupedMediaItemsFlow = MutableSharedFlow>( replay = 1 diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt index bb8419dde5..528fc1da70 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt @@ -80,7 +80,7 @@ class TimelineMediaGalleryDataSourceTest { roomCoroutineScope = backgroundScope, ) ) - sut.start() + sut.start(backgroundScope) assertThat(sut.getLastData()).isEqualTo(AsyncData.Uninitialized) sut.groupedMediaItemsFlow().test { assertThat(awaitItem().isLoading()).isTrue() @@ -95,7 +95,7 @@ class TimelineMediaGalleryDataSourceTest { ) assertThat(sut.getLastData().isSuccess()).isTrue() // Also test that starting again should have no effect - sut.start() + sut.start(backgroundScope) } } // Ensure that the timeline has been closed on flow completion @@ -117,7 +117,7 @@ class TimelineMediaGalleryDataSourceTest { roomCoroutineScope = backgroundScope, ) ) - sut.start() + sut.start(backgroundScope) sut.groupedMediaItemsFlow().test { skipItems(2) sut.loadMore(Timeline.PaginationDirection.BACKWARDS) @@ -140,7 +140,7 @@ class TimelineMediaGalleryDataSourceTest { roomCoroutineScope = backgroundScope, ) ) - sut.start() + sut.start(backgroundScope) sut.groupedMediaItemsFlow().test { skipItems(2) sut.deleteItem(AN_EVENT_ID) @@ -159,7 +159,7 @@ class TimelineMediaGalleryDataSourceTest { roomCoroutineScope = backgroundScope, ) ) - sut.start() + sut.start(backgroundScope) sut.groupedMediaItemsFlow().test { assertThat(awaitItem().isLoading()).isTrue() assertThat(sut.getLastData().isLoading()).isTrue() @@ -181,7 +181,7 @@ class TimelineMediaGalleryDataSourceTest { roomCoroutineScope = backgroundScope, ) ) - sut.start() + sut.start(backgroundScope) sut.groupedMediaItemsFlow().test { assertThat(awaitItem().isLoading()).isTrue() assertThat(sut.getLastData().isLoading()).isTrue() diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt index 44eed2733f..c3f1ab9a0d 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt @@ -50,7 +50,7 @@ class MediaViewerDataSourceTest { val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.setup() + sut.setup(backgroundScope) startLambda.assertions().isCalledOnce() } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSourceTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSourceTest.kt index c6460cb70a..8c0a7c05d0 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSourceTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSourceTest.kt @@ -37,9 +37,9 @@ class SingleMediaGalleryDataSourceTest { val warmUpRule = WarmUpRule() @Test - fun `function start is no op`() { + fun `function start is no op`() = runTest { val sut = SingleMediaGalleryDataSource(aGroupedMediaItems()) - sut.start() + sut.start(backgroundScope) } @Test From 11b9efa2c98ad73faa1e05c840e5c8b8fb45431f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 15:54:33 +0200 Subject: [PATCH 067/471] Migrate to v2 testing APIs --- .../FullscreenAnnouncementViewTest.kt | 25 ++- .../features/call/ui/CallScreenViewTest.kt | 32 ++- .../impl/AccountDeactivationViewTest.kt | 55 +++--- .../forward/impl/ForwardMessagesViewTest.kt | 23 ++- .../ChooseSessionVerificationModeViewTest.kt | 37 ++-- .../impl/filters/RoomListFiltersViewTest.kt | 20 +- .../impl/roomlist/RoomListContextMenuTest.kt | 48 ++--- .../roomlist/RoomListDeclineInviteMenuTest.kt | 33 ++-- .../home/impl/roomlist/RoomListViewTest.kt | 84 ++++---- .../impl/spacefilters/SpaceFiltersViewTest.kt | 34 ++-- .../DeclineAndBlockViewTest.kt | 49 +++-- .../joinroom/impl/JoinRoomViewTest.kt | 121 ++++++------ .../banner/KnockRequestsBannerViewTest.kt | 40 ++-- .../impl/list/KnockRequestsListViewTest.kt | 61 +++--- .../screens/desktop/DesktopNoticeViewTest.kt | 36 ++-- .../impl/screens/error/ErrorViewTest.kt | 32 ++- .../screens/number/EnterNumberViewTest.kt | 40 ++-- .../impl/screens/qrcode/ShowQrCodeViewTest.kt | 20 +- .../screens/root/LinkNewDeviceRootViewTest.kt | 38 ++-- .../impl/screens/scan/ScanQrCodeViewTest.kt | 26 ++- .../impl/share/ShareLocationViewTest.kt | 61 +++--- .../impl/show/ShowLocationViewTest.kt | 57 +++--- .../impl/unlock/keypad/PinKeypadTest.kt | 33 ++-- .../ChooseAccountProviderViewTest.kt | 32 ++- .../loginpassword/LoginPasswordViewTest.kt | 97 +++++---- .../screens/onboarding/OnboardingViewTest.kt | 100 +++++----- .../QrCodeConfirmationViewTest.kt | 26 ++- .../qrcode/error/QrCodeErrorViewTest.kt | 32 ++- .../qrcode/intro/QrCodeIntroViewTest.kt | 36 ++-- .../screens/qrcode/scan/QrCodeScanViewTest.kt | 24 ++- .../features/logout/impl/LogoutViewTest.kt | 49 +++-- .../direct/DefaultDirectLogoutViewTest.kt | 43 ++-- .../messages/impl/MessagesViewTest.kt | 173 ++++++++-------- .../identity/IdentityChangeStateViewTest.kt | 43 ++-- .../ResolveVerifiedUserSendFailureViewTest.kt | 25 ++- .../messages/impl/link/LinkViewTest.kt | 30 ++- .../banner/PinnedMessagesBannerViewTest.kt | 25 ++- .../pinned/list/PinnedMessagesListViewTest.kt | 32 ++- .../DefaultHtmlConverterProviderTest.kt | 12 +- .../impl/timeline/TimelineViewTest.kt | 65 +++--- .../event/TimelineItemPollViewTest.kt | 28 +-- .../components/event/TimelineTextViewTest.kt | 38 ++-- .../timeline/protection/ProtectedViewTest.kt | 27 ++- .../poll/impl/history/PollHistoryViewTest.kt | 56 +++--- .../preferences/impl/about/AboutViewTest.kt | 31 ++- .../impl/advanced/AdvancedSettingsViewTest.kt | 108 +++++----- .../impl/blockedusers/BlockedUserViewTest.kt | 38 ++-- .../developer/DeveloperSettingsViewTest.kt | 38 ++-- .../AppDeveloperSettingsPageTest.kt | 44 ++--- .../NotificationSettingsViewTest.kt | 86 ++++---- .../impl/root/PreferencesRootViewTest.kt | 187 +++++++++--------- .../editprofile/EditUserProfileViewTest.kt | 54 ++--- .../reportroom/impl/ReportRoomViewTest.kt | 43 ++-- .../ChangeRoomPermissionsViewTest.kt | 75 ++++--- .../impl/roles/ChangeRolesViewTest.kt | 111 ++++++----- .../impl/root/RolesAndPermissionsViewTest.kt | 86 ++++---- .../impl/RoomAliasHelperViewTest.kt | 29 ++- .../roomdetails/impl/RoomDetailsViewTest.kt | 149 +++++++------- .../impl/RoomDetailsEditViewTest.kt | 95 +++++---- .../impl/root/RoomDirectoryViewTest.kt | 29 ++- .../impl/RoomMemberModerationViewTest.kt | 85 ++++---- .../SecureBackupEnterRecoveryKeyViewTest.kt | 57 +++--- .../password/ResetIdentityPasswordViewTest.kt | 40 ++-- .../reset/root/ResetIdentityRootViewTest.kt | 44 ++--- .../EditRoomAddressViewTest.kt | 49 +++-- .../ManageAuthorizedSpacesViewTest.kt | 37 ++-- .../impl/root/SecurityAndPrivacyViewTest.kt | 102 +++++----- .../impl/addroom/AddRoomToSpaceViewTest.kt | 45 +++-- .../features/space/impl/root/SpaceViewTest.kt | 85 ++++---- .../JoinBaseRoomByAddressViewTest.kt | 28 ++- .../startchat/impl/root/StartChatViewTest.kt | 52 +++-- .../userprofile/UserProfileViewTest.kt | 98 +++++---- .../shared/blockuser/BlockUserDialogsTest.kt | 33 ++-- .../incoming/IncomingVerificationViewTest.kt | 85 ++++---- .../outgoing/OutgoingVerificationViewTest.kt | 67 ++++--- .../MediaDeleteConfirmationBottomSheetTest.kt | 28 ++- .../details/MediaDetailsBottomSheetTest.kt | 52 +++-- .../impl/viewer/MediaViewerViewTest.kt | 72 +++---- .../markdown/MarkdownTextInputTest.kt | 127 +++++------- .../impl/TroubleshootNotificationsViewTest.kt | 38 ++-- .../impl/history/PushHistoryViewTest.kt | 58 +++--- .../testutils/RobolectricDispatcherCleaner.kt | 8 +- ...nticsNodeInteractionsProviderExtensions.kt | 26 +-- 83 files changed, 2197 insertions(+), 2320 deletions(-) diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementViewTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementViewTest.kt index b69037e61a..b7932898a8 100644 --- a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementViewTest.kt +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/fullscreen/FullscreenAnnouncementViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.announcement.impl.fullscreen import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.announcement.api.Announcement import io.element.android.features.announcement.impl.AnnouncementEvent @@ -20,43 +23,39 @@ 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 FullscreenAnnouncementViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back sends a AnnouncementEvent`() { + fun `clicking on back sends a AnnouncementEvent`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setFullscreenAnnouncementView( + setFullscreenAnnouncementView( anAnnouncementState( announcement = Announcement.Fullscreen.Space, eventSink = eventsRecorder, ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space)) } @Test - fun `clicking on Continue sends a AnnouncementEvent`() { + fun `clicking on Continue sends a AnnouncementEvent`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setFullscreenAnnouncementView( + setFullscreenAnnouncementView( anAnnouncementState( announcement = Announcement.Fullscreen.Space, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(AnnouncementEvent.Continue(Announcement.Fullscreen.Space)) } } -private fun AndroidComposeTestRule.setFullscreenAnnouncementView( +private fun AndroidComposeUiTest.setFullscreenAnnouncementView( state: AnnouncementState, ) { setContent { diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt index 35b90a6716..fed9f90de0 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt @@ -5,6 +5,8 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.call.ui import android.view.KeyEvent @@ -12,8 +14,9 @@ import android.webkit.WebView import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.call.impl.pip.PictureInPictureEvents import io.element.android.features.call.impl.pip.aPictureInPictureState @@ -24,9 +27,7 @@ import io.element.android.features.call.impl.ui.aCallScreenState import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.pressBackKey import org.junit.Assert.assertEquals -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config import org.robolectric.annotation.Implementation @@ -36,32 +37,29 @@ import org.robolectric.shadows.ShadowWebView @RunWith(AndroidJUnit4::class) class CallScreenViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() { + fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() = runAndroidComposeUiTest { val callEvents = EventsRecorder() - rule.setCallScreenView( + setCallScreenView( state = aCallScreenState(eventSink = callEvents), useInspectionMode = true, ) - rule.pressBackKey() + pressBackKey() callEvents.assertEmpty() } @Config(shadows = [RecordingShadowWebView::class]) @Test - fun `pressing back key dispatches escape key events to web view when pip is unsupported`() { - rule.setCallScreenView( + fun `pressing back key dispatches escape key events to web view when pip is unsupported`() = runAndroidComposeUiTest { + setCallScreenView( state = aCallScreenState(), useInspectionMode = false, ) - rule.pressBackKey() + pressBackKey() val dispatchedEvents = RecordingShadowWebView.dispatchedEvents assertEquals(2, dispatchedEvents.size) @@ -73,10 +71,10 @@ class CallScreenViewTest { @Config(shadows = [RecordingShadowWebView::class]) @Test - fun `web view javascript back handler emits pip event when pip is supported`() { + fun `web view javascript back handler emits pip event when pip is supported`() = runAndroidComposeUiTest { val pipEvents = EventsRecorder() - rule.setCallScreenView( + setCallScreenView( state = aCallScreenState(), useInspectionMode = false, pipState = aPictureInPictureState( @@ -85,7 +83,7 @@ class CallScreenViewTest { ), ) - rule.runOnIdle { + runOnIdle { RecordingShadowWebView.invokeJavascriptBackHandler() } @@ -95,7 +93,7 @@ class CallScreenViewTest { } } -private fun AndroidComposeTestRule.setCallScreenView( +private fun AndroidComposeUiTest.setCallScreenView( state: io.element.android.features.call.impl.ui.CallScreenState, useInspectionMode: Boolean, pipState: io.element.android.features.call.impl.pip.PictureInPictureState = aPictureInPictureState(supportPip = false), diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt index 26c942da1f..c672fd666b 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.logout.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.deactivation.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -26,33 +29,29 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressTag -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class AccountDeactivationViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState(eventSink = eventsRecorder), onBackClick = it, ) - rule.pressBack() + pressBack() } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on Deactivate emits the expected Event`() { + fun `clicking on Deactivate emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( password = A_PASSWORD, @@ -60,14 +59,14 @@ class AccountDeactivationViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_delete) + clickOn(CommonStrings.action_delete) eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) } @Test - fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() { + fun `clicking on Deactivate on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( password = A_PASSWORD, @@ -76,14 +75,14 @@ class AccountDeactivationViewTest { eventSink = eventsRecorder, ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(false)) } @Test - fun `clicking on retry on the confirmation dialog emits the expected Event`() { + fun `clicking on retry on the confirmation dialog emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( password = A_PASSWORD, @@ -92,26 +91,26 @@ class AccountDeactivationViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_retry) + clickOn(CommonStrings.action_retry) eventsRecorder.assertSingle(AccountDeactivationEvents.DeactivateAccount(true)) } @Test - fun `switching on the erase all switch emits the expected Event`() { + fun `switching on the erase all switch emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_deactivate_account_delete_all_messages) + clickOn(R.string.screen_deactivate_account_delete_all_messages) eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(true)) } @Test - fun `switching off the erase all switch emits the expected Event`() { + fun `switching off the erase all switch emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( eraseData = true, @@ -119,15 +118,15 @@ class AccountDeactivationViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_deactivate_account_delete_all_messages) + clickOn(R.string.screen_deactivate_account_delete_all_messages) eventsRecorder.assertSingle(AccountDeactivationEvents.SetEraseData(false)) } @Config(qualifiers = "h1024dp") @Test - fun `typing text in the password field emits the expected Event`() { + fun `typing text in the password field emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAccountDeactivationView( + setAccountDeactivationView( state = anAccountDeactivationState( deactivateFormState = aDeactivateFormState( password = A_PASSWORD, @@ -135,12 +134,12 @@ class AccountDeactivationViewTest { eventSink = eventsRecorder, ), ) - rule.onNodeWithTag(TestTags.loginPassword.value).performTextInput("A") + onNodeWithTag(TestTags.loginPassword.value).performTextInput("A") eventsRecorder.assertSingle(AccountDeactivationEvents.SetPassword("A$A_PASSWORD")) } } -private fun AndroidComposeTestRule.setAccountDeactivationView( +private fun AndroidComposeUiTest.setAccountDeactivationView( state: AccountDeactivationState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt index f1e9bd8fc6..57a9f65099 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.forward.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId @@ -21,34 +24,30 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressTag -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ForwardMessagesViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `cancel error emits the expected event`() { + fun `cancel error emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setForwardMessagesView( + setForwardMessagesView( aForwardMessagesState( forwardAction = AsyncAction.Failure(AN_EXCEPTION), eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(ForwardMessagesEvents.ClearError) } @Test - fun `success invokes onForwardSuccess`() { + fun `success invokes onForwardSuccess`() = runAndroidComposeUiTest { val data = listOf(A_ROOM_ID) val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam?>(data) { callback -> - rule.setForwardMessagesView( + setForwardMessagesView( aForwardMessagesState( forwardAction = AsyncAction.Success(data), eventSink = eventsRecorder @@ -59,7 +58,7 @@ class ForwardMessagesViewTest { } } -private fun AndroidComposeTestRule.setForwardMessagesView( +private fun AndroidComposeUiTest.setForwardMessagesView( state: ForwardMessagesState, onForwardSuccess: (List) -> Unit = EnsureNeverCalledWithParam(), ) { diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt index 521bf91b37..6e74f58f66 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.ftue.impl.sessionverification.choosemode import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.ftue.impl.R import io.element.android.libraries.architecture.AsyncData @@ -18,65 +21,61 @@ import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class ChooseSessionVerificationModeViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Config(qualifiers = "h1024dp") @Test - fun `clicking on learn more invokes the expected callback`() { + fun `clicking on learn more invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setChooseSelfVerificationModeView( + setChooseSelfVerificationModeView( aChooseSelfVerificationModeState(), onLearnMoreClick = callback, ) - rule.clickOn(CommonStrings.action_learn_more) + clickOn(CommonStrings.action_learn_more) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on use another device calls the callback`() { + fun `clicking on use another device calls the callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setChooseSelfVerificationModeView( + setChooseSelfVerificationModeView( aChooseSelfVerificationModeState(AsyncData.Success(aButtonsState(canUseAnotherDevice = true))), onUseAnotherDevice = callback, ) - rule.clickOn(R.string.screen_identity_use_another_device) + clickOn(R.string.screen_identity_use_another_device) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on enter recovery key calls the callback`() { + fun `clicking on enter recovery key calls the callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setChooseSelfVerificationModeView( + setChooseSelfVerificationModeView( aChooseSelfVerificationModeState(AsyncData.Success(aButtonsState(canUseRecoveryKey = true))), onEnterRecoveryKey = callback, ) - rule.clickOn(R.string.screen_identity_confirmation_use_recovery_key) + clickOn(R.string.screen_identity_confirmation_use_recovery_key) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on cannot confirm calls the reset keys callback`() { + fun `clicking on cannot confirm calls the reset keys callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setChooseSelfVerificationModeView( + setChooseSelfVerificationModeView( aChooseSelfVerificationModeState(), onResetKey = callback, ) - rule.clickOn(R.string.screen_identity_confirmation_cannot_confirm) + clickOn(R.string.screen_identity_confirmation_cannot_confirm) } } - private fun AndroidComposeTestRule.setChooseSelfVerificationModeView( + private fun AndroidComposeUiTest.setChooseSelfVerificationModeView( state: ChooseSelfVerificationModeState, onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onUseAnotherDevice: () -> Unit = EnsureNeverCalled(), diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersViewTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersViewTest.kt index 4c361b47f3..de5760c1bd 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersViewTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/filters/RoomListFiltersViewTest.kt @@ -6,10 +6,13 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.home.impl.filters import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.home.impl.R import io.element.android.features.home.impl.filters.selection.FilterSelectionState @@ -17,23 +20,20 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressTag -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomListFiltersViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on filters generates expected Event`() { + fun `clicking on filters generates expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { RoomListFiltersView( state = aRoomListFiltersState(eventSink = eventsRecorder), ) } - rule.clickOn(R.string.screen_roomlist_filter_rooms) + clickOn(R.string.screen_roomlist_filter_rooms) eventsRecorder.assertList( listOf( RoomListFiltersEvent.ToggleFilter(RoomListFilter.Rooms), @@ -42,9 +42,9 @@ class RoomListFiltersViewTest { } @Test - fun `clicking on clear filters generates expected Event`() { + fun `clicking on clear filters generates expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { RoomListFiltersView( state = aRoomListFiltersState( filterSelectionStates = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = true) }, @@ -52,7 +52,7 @@ class RoomListFiltersViewTest { ), ) } - rule.pressTag(TestTags.homeScreenClearFilters.value) + pressTag(TestTags.homeScreenClearFilters.value) eventsRecorder.assertList( listOf( RoomListFiltersEvent.ClearSelectedFilters, diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenuTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenuTest.kt index 6be5fe4c16..5fa2adf9d6 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenuTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenuTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.home.impl.roomlist import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.home.impl.R import io.element.android.libraries.matrix.api.core.RoomId @@ -20,23 +23,20 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomListContextMenuTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on Mark as read generates expected Events`() { + fun `clicking on Mark as read generates expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown(hasNewContent = true) - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, eventSink = eventsRecorder, ) - rule.clickOn(R.string.screen_roomlist_mark_as_read) + clickOn(R.string.screen_roomlist_mark_as_read) eventsRecorder.assertList( listOf( RoomListEvent.HideContextMenu, @@ -46,14 +46,14 @@ class RoomListContextMenuTest { } @Test - fun `clicking on Mark as unread generates expected Events`() { + fun `clicking on Mark as unread generates expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown(hasNewContent = false) - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, eventSink = eventsRecorder, ) - rule.clickOn(R.string.screen_roomlist_mark_as_unread) + clickOn(R.string.screen_roomlist_mark_as_unread) eventsRecorder.assertList( listOf( RoomListEvent.HideContextMenu, @@ -63,14 +63,14 @@ class RoomListContextMenuTest { } @Test - fun `clicking on Leave room generates expected Events`() { + fun `clicking on Leave room generates expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown(isDm = false) - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, eventSink = eventsRecorder, ) - rule.clickOn(CommonStrings.action_leave_room) + clickOn(CommonStrings.action_leave_room) eventsRecorder.assertList( listOf( RoomListEvent.HideContextMenu, @@ -80,48 +80,48 @@ class RoomListContextMenuTest { } @Test - fun `clicking on Report room invokes the expected callback and generates expected Event`() { + fun `clicking on Report room invokes the expected callback and generates expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown() val callback = EnsureCalledOnceWithParam(contextMenu.roomId, Unit) - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, canReportRoom = true, eventSink = eventsRecorder, onRoomSettingsClick = EnsureNeverCalledWithParam(), onReportRoomClick = callback, ) - rule.clickOn(CommonStrings.action_report_room) + clickOn(CommonStrings.action_report_room) eventsRecorder.assertSingle(RoomListEvent.HideContextMenu) callback.assertSuccess() } @Test - fun `clicking on Settings invokes the expected callback and generates expected Event`() { + fun `clicking on Settings invokes the expected callback and generates expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown() val callback = EnsureCalledOnceWithParam(contextMenu.roomId, Unit) - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, eventSink = eventsRecorder, onRoomSettingsClick = callback, ) - rule.clickOn(CommonStrings.common_settings) + clickOn(CommonStrings.common_settings) eventsRecorder.assertSingle(RoomListEvent.HideContextMenu) callback.assertSuccess() } @Test - fun `clicking on Favourites generates expected Event`() { + fun `clicking on Favourites generates expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val contextMenu = aContextMenuShown(isDm = false, isFavorite = false) val callback = EnsureNeverCalledWithParam() - rule.setRoomListContextMenu( + setRoomListContextMenu( contextMenu = contextMenu, eventSink = eventsRecorder, onRoomSettingsClick = callback, ) - rule.clickOn(CommonStrings.common_favourite) + clickOn(CommonStrings.common_favourite) eventsRecorder.assertList( listOf( RoomListEvent.SetRoomIsFavorite(contextMenu.roomId, true), @@ -129,7 +129,7 @@ class RoomListContextMenuTest { ) } - private fun AndroidComposeTestRule<*, *>.setRoomListContextMenu( + private fun AndroidComposeUiTest.setRoomListContextMenu( contextMenu: RoomListState.ContextMenu.Shown, canReportRoom: Boolean = false, eventSink: (RoomListEvent) -> Unit, diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenuTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenuTest.kt index d7f509fda4..c8bba05e52 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenuTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListDeclineInviteMenuTest.kt @@ -6,10 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.home.impl.roomlist -import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.home.impl.model.aRoomListRoomSummary import io.element.android.libraries.ui.strings.CommonStrings @@ -18,19 +20,16 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomListDeclineInviteMenuTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on decline emits the expected Events`() { + fun `clicking on decline emits the expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary()) - rule.setSafeContent { + setSafeContent { RoomListDeclineInviteMenu( menu = menu, canReportRoom = false, @@ -38,7 +37,7 @@ class RoomListDeclineInviteMenuTest { eventSink = eventsRecorder, ) } - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertList( listOf( RoomListEvent.HideDeclineInviteMenu, @@ -48,10 +47,10 @@ class RoomListDeclineInviteMenuTest { } @Test - fun `clicking on decline and block when canReportRoom=true, it emits the expected Events and callback`() { + fun `clicking on decline and block when canReportRoom=true, it emits the expected Events and callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary()) - rule.setSafeContent { + setSafeContent { RoomListDeclineInviteMenu( menu = menu, canReportRoom = true, @@ -59,16 +58,16 @@ class RoomListDeclineInviteMenuTest { eventSink = eventsRecorder, ) } - rule.clickOn(CommonStrings.action_decline_and_block) + clickOn(CommonStrings.action_decline_and_block) val expectedEvents = listOf(RoomListEvent.HideDeclineInviteMenu) eventsRecorder.assertList(expectedEvents) } @Test - fun `clicking on decline and block when canReportRoom=false, it emits the expected Events`() { + fun `clicking on decline and block when canReportRoom=false, it emits the expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary()) - rule.setSafeContent { + setSafeContent { RoomListDeclineInviteMenu( menu = menu, canReportRoom = false, @@ -76,7 +75,7 @@ class RoomListDeclineInviteMenuTest { eventSink = eventsRecorder, ) } - rule.clickOn(CommonStrings.action_decline_and_block) + clickOn(CommonStrings.action_decline_and_block) val expectedEvents = listOf( RoomListEvent.HideDeclineInviteMenu, RoomListEvent.DeclineInvite(menu.roomSummary, blockUser = true), @@ -85,10 +84,10 @@ class RoomListDeclineInviteMenuTest { } @Test - fun `clicking on cancel emits the expected Event`() { + fun `clicking on cancel emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val menu = RoomListState.DeclineInviteMenu.Shown(roomSummary = aRoomListRoomSummary()) - rule.setSafeContent { + setSafeContent { RoomListDeclineInviteMenu( menu = menu, canReportRoom = false, @@ -96,7 +95,7 @@ class RoomListDeclineInviteMenuTest { eventSink = eventsRecorder, ) } - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertList(listOf(RoomListEvent.HideDeclineInviteMenu)) } } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt index 8402a921ca..b8d61994fa 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt @@ -6,16 +6,19 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.home.impl.roomlist import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.longClick import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.home.impl.HomeView import io.element.android.features.home.impl.R @@ -32,22 +35,17 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class RoomListViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Config(qualifiers = "h1024dp") @Test - fun `displaying the view automatically sends a couple of UpdateVisibleRangeEvents`() { + fun `displaying the view automatically sends a couple of UpdateVisibleRangeEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomListView( + setRoomListView( state = aRoomListState( contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), eventSink = eventsRecorder, @@ -62,9 +60,9 @@ class RoomListViewTest { } @Test - fun `clicking on close recovery key banner emits the expected Event`() { + fun `clicking on close recovery key banner emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomListView( + setRoomListView( state = aRoomListState( contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), eventSink = eventsRecorder, @@ -74,15 +72,15 @@ class RoomListViewTest { // Remove automatic initial events eventsRecorder.clear() - val close = rule.activity.getString(CommonStrings.action_close) - rule.onNodeWithContentDescription(close).performClick() + val close = activity!!.getString(CommonStrings.action_close) + onNodeWithContentDescription(close).performClick() eventsRecorder.assertSingle(RoomListEvent.DismissBanner) } @Test - fun `clicking on close setup key banner emits the expected Event`() { + fun `clicking on close setup key banner emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomListView( + setRoomListView( state = aRoomListState( contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SetUpRecovery), eventSink = eventsRecorder, @@ -92,16 +90,16 @@ class RoomListViewTest { // Remove automatic initial events eventsRecorder.clear() - val close = rule.activity.getString(CommonStrings.action_close) - rule.onNodeWithContentDescription(close).performClick() + val close = activity!!.getString(CommonStrings.action_close) + onNodeWithContentDescription(close).performClick() eventsRecorder.assertSingle(RoomListEvent.DismissBanner) } @Test - fun `clicking on continue recovery key banner invokes the expected callback`() { + fun `clicking on continue recovery key banner invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnce { callback -> - rule.setRoomListView( + setRoomListView( state = aRoomListState( contentState = aRoomsContentState(securityBannerState = SecurityBannerState.RecoveryKeyConfirmation), eventSink = eventsRecorder, @@ -112,17 +110,17 @@ class RoomListViewTest { // Remove automatic initial events eventsRecorder.clear() - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertEmpty() } } @Test - fun `clicking on continue setup key banner invokes the expected callback`() { + fun `clicking on continue setup key banner invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnce { callback -> - rule.setRoomListView( + setRoomListView( state = aRoomListState( contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SetUpRecovery), eventSink = eventsRecorder, @@ -131,28 +129,28 @@ class RoomListViewTest { ) // Remove automatic initial events eventsRecorder.clear() - rule.clickOn(R.string.banner_set_up_recovery_submit) + clickOn(R.string.banner_set_up_recovery_submit) eventsRecorder.assertEmpty() } } @Test - fun `clicking on start chat when the session has no room invokes the expected callback`() { + fun `clicking on start chat when the session has no room invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setRoomListView( + setRoomListView( state = aRoomListState( eventSink = eventsRecorder, contentState = anEmptyContentState(), ), onCreateRoomClick = callback, ) - rule.clickOn(CommonStrings.action_start_chat) + clickOn(CommonStrings.action_start_chat) } } @Test - fun `clicking on a room invokes the expected callback`() { + fun `clicking on a room invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomListState( eventSink = eventsRecorder, @@ -161,7 +159,7 @@ class RoomListViewTest { it.displayType == RoomSummaryDisplayType.ROOM } ensureCalledOnceWithParam(room0.roomId) { callback -> - rule.setRoomListView( + setRoomListView( state = state, onRoomClick = callback, ) @@ -169,14 +167,14 @@ class RoomListViewTest { // Remove automatic initial events eventsRecorder.clear() - rule.onNodeWithText(room0.latestEvent.content().toString()).performClick() + onNodeWithText(room0.latestEvent.content().toString()).performClick() } eventsRecorder.assertEmpty() } @Test - fun `clicking on a room twice invokes the expected callback only once`() { + fun `clicking on a room twice invokes the expected callback only once`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomListState( eventSink = eventsRecorder, @@ -185,13 +183,13 @@ class RoomListViewTest { it.displayType == RoomSummaryDisplayType.ROOM } ensureCalledOnceWithParam(room0.roomId) { callback -> - rule.setRoomListView( + setRoomListView( state = state, onRoomClick = callback, ) // Remove automatic initial events eventsRecorder.clear() - rule.onNodeWithText(room0.latestEvent.content().toString()) + onNodeWithText(room0.latestEvent.content().toString()) .performClick() .performClick() } @@ -199,7 +197,7 @@ class RoomListViewTest { } @Test - fun `long clicking on a room emits the expected Event`() { + fun `long clicking on a room emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomListState( eventSink = eventsRecorder, @@ -207,18 +205,18 @@ class RoomListViewTest { val room0 = state.contentAsRooms().summaries.first { it.displayType == RoomSummaryDisplayType.ROOM } - rule.setRoomListView( + setRoomListView( state = state, ) // Remove automatic initial events eventsRecorder.clear() - rule.onNodeWithText(room0.latestEvent.content().toString()).performTouchInput { longClick() } + onNodeWithText(room0.latestEvent.content().toString()).performTouchInput { longClick() } eventsRecorder.assertSingle(RoomListEvent.ShowContextMenu(room0)) } @Test - fun `clicking on a room setting invokes the expected callback and emits expected Event`() { + fun `clicking on a room setting invokes the expected callback and emits expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomListState( contextMenu = aContextMenuShown(), @@ -226,7 +224,7 @@ class RoomListViewTest { ) val room0 = (state.contextMenu as RoomListState.ContextMenu.Shown).roomId ensureCalledOnceWithParam(room0) { callback -> - rule.setRoomListView( + setRoomListView( state = state, onRoomSettingsClick = callback, ) @@ -234,14 +232,14 @@ class RoomListViewTest { // Remove automatic initial events eventsRecorder.clear() - rule.clickOn(CommonStrings.common_settings) + clickOn(CommonStrings.common_settings) } eventsRecorder.assertSingle(RoomListEvent.HideContextMenu) } @Test - fun `clicking on accept and decline invite emits the expected Events`() { + fun `clicking on accept and decline invite emits the expected Events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomListState( eventSink = eventsRecorder, @@ -249,13 +247,13 @@ class RoomListViewTest { val invitedRoom = state.contentAsRooms().summaries.first { it.displayType == RoomSummaryDisplayType.INVITE } - rule.setRoomListView(state = state) + setRoomListView(state = state) // Remove automatic initial events eventsRecorder.clear() - rule.clickOn(CommonStrings.action_accept) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_accept) + clickOn(CommonStrings.action_decline) eventsRecorder.assertList( listOf( RoomListEvent.AcceptInvite(invitedRoom), @@ -265,7 +263,7 @@ class RoomListViewTest { } } -private fun AndroidComposeTestRule.setRoomListView( +private fun AndroidComposeUiTest.setRoomListView( state: RoomListState, onRoomClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onSettingsClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersViewTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersViewTest.kt index 5c1325b107..d612d765b6 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersViewTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersViewTest.kt @@ -5,34 +5,32 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.home.impl.spacefilters import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.test.A_ROOM_ALIAS import io.element.android.tests.testutils.EventsRecorder -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SpaceFiltersViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on a filter with alias shows display name and alias`() { + fun `clicking on a filter with alias shows display name and alias`() = runAndroidComposeUiTest { val filter = aSpaceServiceFilter( displayName = "Test Space", canonicalAlias = A_ROOM_ALIAS, ) val eventsRecorder = EventsRecorder() - rule.setSpaceFiltersView( + setSpaceFiltersView( state = aSelectingSpaceFiltersState( availableFilters = listOf(filter), eventSink = eventsRecorder, @@ -40,20 +38,20 @@ class SpaceFiltersViewTest { ) // Both display name and alias should be visible - rule.onNodeWithText(filter.spaceRoom.displayName).assertExists() - rule.onNodeWithText(A_ROOM_ALIAS.value).assertExists() + onNodeWithText(filter.spaceRoom.displayName).assertExists() + onNodeWithText(A_ROOM_ALIAS.value).assertExists() - rule.onNodeWithText(filter.spaceRoom.displayName).performClick() + onNodeWithText(filter.spaceRoom.displayName).performClick() eventsRecorder.assertSingle(SpaceFiltersEvent.Selecting.SelectFilter(filter)) } @Test - fun `multiple filters are displayed and clickable`() { + fun `multiple filters are displayed and clickable`() = runAndroidComposeUiTest { val filter1 = aSpaceServiceFilter(displayName = "Space One") val filter2 = aSpaceServiceFilter(displayName = "Space Two") val eventsRecorder = EventsRecorder() - rule.setSpaceFiltersView( + setSpaceFiltersView( state = aSelectingSpaceFiltersState( availableFilters = listOf(filter1, filter2), eventSink = eventsRecorder, @@ -61,17 +59,17 @@ class SpaceFiltersViewTest { ) // Both filters should be visible - rule.onNodeWithText(filter1.spaceRoom.displayName).assertExists() - rule.onNodeWithText(filter2.spaceRoom.displayName).assertExists() + onNodeWithText(filter1.spaceRoom.displayName).assertExists() + onNodeWithText(filter2.spaceRoom.displayName).assertExists() // Click on second filter - rule.onNodeWithText(filter2.spaceRoom.displayName).performClick() + onNodeWithText(filter2.spaceRoom.displayName).performClick() eventsRecorder.assertSingle(SpaceFiltersEvent.Selecting.SelectFilter(filter2)) } } -private fun AndroidComposeTestRule.setSpaceFiltersView( +private fun AndroidComposeUiTest.setSpaceFiltersView( state: SpaceFiltersState, ) { setContent { diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt index 299fec8565..e915696de4 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/declineandblock/DeclineAndBlockViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.invite.impl.declineandblock import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.invite.impl.R import io.element.android.libraries.ui.strings.CommonStrings @@ -21,98 +24,94 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class DeclineAndBlockViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke the expected callback`() { + fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( eventSink = eventsRecorder, ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on decline when enabled emits the expected event`() { + fun `clicking on decline when enabled emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( blockUser = true, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertSingle(DeclineAndBlockEvents.Decline) } @Test - fun `clicking on decline when disabled does not emit event`() { + fun `clicking on decline when disabled does not emit event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( blockUser = false, reportRoom = false, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertEmpty() } @Test - fun `clicking on block option emits the expected event`() { + fun `clicking on block option emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( blockUser = true, eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_decline_and_block_block_user_option_title) + clickOn(R.string.screen_decline_and_block_block_user_option_title) eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleBlockUser) } @Test - fun `clicking on report room option emits the expected event`() { + fun `clicking on report room option emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( reportRoom = true, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_report_room) + clickOn(CommonStrings.action_report_room) eventsRecorder.assertSingle(DeclineAndBlockEvents.ToggleReportRoom) } @Test - fun `typing text in the reason field emits the expected Event`() { + fun `typing text in the reason field emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDeclineAndBlockView( + setDeclineAndBlockView( aDeclineAndBlockState( reportRoom = true, reportReason = "", eventSink = eventsRecorder, ), ) - rule.onNodeWithText("").performTextInput("Spam!") + onNodeWithText("").performTextInput("Spam!") eventsRecorder.assertSingle(DeclineAndBlockEvents.UpdateReportReason("Spam!")) } } -private fun AndroidComposeTestRule.setDeclineAndBlockView( +private fun AndroidComposeUiTest.setDeclineAndBlockView( state: DeclineAndBlockState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt index 0a3b1ca3c6..e60d7da691 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.joinroom.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.invite.api.InviteData import io.element.android.features.invite.test.anInviteData @@ -26,116 +29,112 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class JoinRoomViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke the expected callback`() { + fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( eventSink = eventsRecorder, ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on Join room on CanJoin room emits the expected Event`() { + fun `clicking on Join room on CanJoin room emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_join_room_join_action) + clickOn(R.string.screen_join_room_join_action) eventsRecorder.assertSingle(JoinRoomEvents.JoinRoom) } @Test - fun `clicking on Knock room on CanKnock room emits the expected Event`() { + fun `clicking on Knock room on CanKnock room emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), knockMessage = "Knock knock", eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_join_room_knock_action) + clickOn(R.string.screen_join_room_knock_action) eventsRecorder.assertSingle(JoinRoomEvents.KnockRoom) } @Test - fun `clicking on closing Knock error emits the expected Event`() { + fun `clicking on closing Knock error emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), knockAction = AsyncAction.Failure(Exception("Error")), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) } @Test - fun `clicking on cancel knock request emit the expected Event`() { + fun `clicking on cancel knock request emit the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_join_room_cancel_knock_action) + clickOn(R.string.screen_join_room_cancel_knock_action) eventsRecorder.assertSingle(JoinRoomEvents.CancelKnock(true)) } @Test - fun `clicking on closing Cancel Knock error emits the expected Event`() { + fun `clicking on closing Cancel Knock error emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked), cancelKnockAction = AsyncAction.Failure(Exception("Error")), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) } @Test - fun `clicking on closing Join error emits the expected Event`() { + fun `clicking on closing Join error emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanKnock), joinAction = AsyncAction.Failure(Exception("Error")), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(JoinRoomEvents.ClearActionStates) } @Test - fun `when joining room is successful, the expected callback is invoked`() { + fun `when joining room is successful, the expected callback is invoked`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( joinAction = AsyncAction.Success(Unit), eventSink = eventsRecorder, @@ -146,53 +145,55 @@ class JoinRoomViewTest { } @Test - fun `clicking on Accept when JoinAuthorisationStatus is IsInvited emits the expected Event`() { + fun `clicking on Accept when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val inviteData = anInviteData() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, null)), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_accept) + clickOn(CommonStrings.action_accept) eventsRecorder.assertSingle(JoinRoomEvents.AcceptInvite(inviteData)) } @Test - fun `clicking on Decline when JoinAuthorisationStatus is IsInvited emits the expected Event`() { + fun `clicking on Decline when JoinAuthorisationStatus is IsInvited emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val inviteData = anInviteData() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, null)), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, false)) } @Test fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and can report room, the expected callback is invoked`() { - val eventsRecorder = EventsRecorder(expectEvents = false) - val inviteData = anInviteData() - val joinRoomState = aJoinRoomState( - contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())), - canReportRoom = true, - eventSink = eventsRecorder, - ) - ensureCalledOnceWithParam(inviteData) { - rule.setJoinRoomView( - state = joinRoomState, - onDeclineInviteAndBlockUser = it, + runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder(expectEvents = false) + val inviteData = anInviteData() + val joinRoomState = aJoinRoomState( + contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(inviteData, aRoomMember().toInviteSender())), + canReportRoom = true, + eventSink = eventsRecorder, ) - rule.clickOn(R.string.screen_join_room_decline_and_block_button_title) + ensureCalledOnceWithParam(inviteData) { + setJoinRoomView( + state = joinRoomState, + onDeclineInviteAndBlockUser = it, + ) + clickOn(R.string.screen_join_room_decline_and_block_button_title) + } } } @Test - fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and cant report room, emits the expected Event`() { + fun `clicking on Decline and block when JoinAuthorisationStatus is IsInvited and cant report room, emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val inviteData = anInviteData() val joinRoomState = aJoinRoomState( @@ -200,29 +201,29 @@ class JoinRoomViewTest { canReportRoom = false, eventSink = eventsRecorder, ) - rule.setJoinRoomView(state = joinRoomState) - rule.clickOn(R.string.screen_join_room_decline_and_block_button_title) + setJoinRoomView(state = joinRoomState) + clickOn(R.string.screen_join_room_decline_and_block_button_title) eventsRecorder.assertSingle(JoinRoomEvents.DeclineInvite(inviteData, true)) } @Test - fun `clicking on Retry when an error occurs emits the expected Event`() { + fun `clicking on Retry when an error occurs emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aFailureContentState(), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_retry) + clickOn(CommonStrings.action_retry) eventsRecorder.assertSingle(JoinRoomEvents.RetryFetchingContent) } @Test - fun `clicking on ok when user is unauthorized the expected callback`() { + fun `clicking on ok when user is unauthorized the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(), joinAction = AsyncAction.Failure(JoinRoom.Failures.UnauthorizedJoin), @@ -230,25 +231,25 @@ class JoinRoomViewTest { ), onBackClick = it ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) } } @Test - fun `clicking on forget when user is banned invokes the expected callback`() { + fun `clicking on forget when user is banned invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomView( + setJoinRoomView( aJoinRoomState( contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(null, null)), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_join_room_forget_action) + clickOn(R.string.screen_join_room_forget_action) eventsRecorder.assertSingle(JoinRoomEvents.ForgetRoom) } } -private fun AndroidComposeTestRule.setJoinRoomView( +private fun AndroidComposeUiTest.setJoinRoomView( state: JoinRoomState, onBackClick: () -> Unit = EnsureNeverCalled(), onJoinSuccess: () -> Unit = EnsureNeverCalled(), diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt index a9fea0905e..fc1600d8c8 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.knockrequests.impl.banner import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.knockrequests.impl.R import io.element.android.features.knockrequests.impl.data.aKnockRequestPresentable @@ -21,35 +24,30 @@ import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class KnockRequestsBannerViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on view on single request invoke the expected callback`() { + fun `clicking on view on single request invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setKnockRequestsBannerView( + setKnockRequestsBannerView( state = aKnockRequestsBannerState( eventSink = eventsRecorder, ), onViewRequestsClick = it ) - rule.clickOn(R.string.screen_room_single_knock_request_view_button_title) + clickOn(R.string.screen_room_single_knock_request_view_button_title) } } @Test - fun `clicking on view all when multiple requests invoke the expected callback`() { + fun `clicking on view all when multiple requests invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setKnockRequestsBannerView( + setKnockRequestsBannerView( state = aKnockRequestsBannerState( knockRequests = listOf( aKnockRequestPresentable(displayName = "Alice"), @@ -60,37 +58,37 @@ class KnockRequestsBannerViewTest { ), onViewRequestsClick = it ) - rule.clickOn(R.string.screen_room_multiple_knock_requests_view_all_button_title) + clickOn(R.string.screen_room_multiple_knock_requests_view_all_button_title) } } @Test - fun `clicking on accept on a single request emit the expected event`() { + fun `clicking on accept on a single request emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setKnockRequestsBannerView( + setKnockRequestsBannerView( state = aKnockRequestsBannerState( eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_accept) + clickOn(CommonStrings.action_accept) eventsRecorder.assertSingle(KnockRequestsBannerEvents.AcceptSingleRequest) } @Test - fun `clicking on dismiss emit the expected event`() { + fun `clicking on dismiss emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setKnockRequestsBannerView( + setKnockRequestsBannerView( state = aKnockRequestsBannerState( eventSink = eventsRecorder, ), ) - val close = rule.activity.getString(CommonStrings.action_close) - rule.onNodeWithContentDescription(close).performClick() + val close = activity!!.getString(CommonStrings.action_close) + onNodeWithContentDescription(close).performClick() eventsRecorder.assertSingle(KnockRequestsBannerEvents.Dismiss) } } -private fun AndroidComposeTestRule.setKnockRequestsBannerView( +private fun AndroidComposeUiTest.setKnockRequestsBannerView( state: KnockRequestsBannerState, onViewRequestsClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt index 188dcc7e56..14cac7a9b7 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.knockrequests.impl.list import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.knockrequests.impl.R import io.element.android.features.knockrequests.impl.data.aKnockRequestPresentable @@ -23,90 +26,86 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import kotlinx.collections.immutable.persistentListOf -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class KnockRequestsListViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke the expected callback`() { + fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( eventSink = eventsRecorder, ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on accept emit the expected event`() { + fun `clicking on accept emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(persistentListOf(knockRequest)), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_accept) + clickOn(CommonStrings.action_accept) eventsRecorder.assertSingle(KnockRequestsListEvents.Accept(knockRequest)) } @Test - fun `clicking on decline emit the expected event`() { + fun `clicking on decline emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(persistentListOf(knockRequest)), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertSingle(KnockRequestsListEvents.Decline(knockRequest)) } @Test - fun `clicking on decline and ban emit the expected event`() { + fun `clicking on decline and ban emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequest = aKnockRequestPresentable() - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(persistentListOf(knockRequest)), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_knock_requests_list_decline_and_ban_action_title) + clickOn(R.string.screen_knock_requests_list_decline_and_ban_action_title) eventsRecorder.assertSingle(KnockRequestsListEvents.DeclineAndBan(knockRequest)) } @Test - fun `clicking on accept all emit the expected event`() { + fun `clicking on accept all emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(knockRequests), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_knock_requests_list_accept_all_button_title) + clickOn(R.string.screen_knock_requests_list_accept_all_button_title) eventsRecorder.assertSingle(KnockRequestsListEvents.AcceptAll) } @Test - fun `retry on async view retry emit the expected event`() { + fun `retry on async view retry emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(knockRequests), asyncAction = AsyncAction.Failure(RuntimeException("Failed to accept all")), @@ -114,15 +113,15 @@ class KnockRequestsListViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_retry) + clickOn(CommonStrings.action_retry) eventsRecorder.assertSingle(KnockRequestsListEvents.RetryCurrentAction) } @Test - fun `canceling async view emit the expected event`() { + fun `canceling async view emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(knockRequests), asyncAction = AsyncAction.Failure(RuntimeException("Failed to accept all")), @@ -130,15 +129,15 @@ class KnockRequestsListViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(KnockRequestsListEvents.ResetCurrentAction) } @Test - fun `confirming async view emit the expected event`() { + fun `confirming async view emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val knockRequests = persistentListOf(aKnockRequestPresentable(), aKnockRequestPresentable()) - rule.setKnockRequestsListView( + setKnockRequestsListView( aKnockRequestsListState( knockRequests = AsyncData.Success(knockRequests), asyncAction = AsyncAction.ConfirmingNoParams, @@ -146,12 +145,12 @@ class KnockRequestsListViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_knock_requests_list_accept_all_alert_confirm_button_title) + clickOn(R.string.screen_knock_requests_list_accept_all_alert_confirm_button_title) eventsRecorder.assertSingle(KnockRequestsListEvents.ConfirmCurrentAction) } } -private fun AndroidComposeTestRule.setKnockRequestsListView( +private fun AndroidComposeUiTest.setKnockRequestsListView( state: KnockRequestsListState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeViewTest.kt index ac0a129f49..7609acf809 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/desktop/DesktopNoticeViewTest.kt @@ -5,11 +5,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.desktop import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.linknewdevice.impl.R import io.element.android.tests.testutils.EnsureNeverCalled @@ -18,42 +21,37 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 DesktopNoticeViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( state = aDesktopNoticeState(), onBackClicked = callback, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on back button clicked - calls the expected callback`() { + fun `on back button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( state = aDesktopNoticeState(), onBackClicked = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `when can continue - calls the expected callback`() { + fun `when can continue - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( state = aDesktopNoticeState(canContinue = true), onReadyToScanClick = callback, ) @@ -61,16 +59,16 @@ class DesktopNoticeViewTest { } @Test - fun `on submit button clicked - emits the Continue event`() { + fun `on submit button clicked - emits the Continue event`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder() - rule.setView( + setView( state = aDesktopNoticeState(eventSink = eventRecorder), ) - rule.clickOn(R.string.screen_link_new_device_desktop_submit) + clickOn(R.string.screen_link_new_device_desktop_submit) eventRecorder.assertSingle(DesktopNoticeEvent.Continue) } - private fun AndroidComposeTestRule.setView( + private fun AndroidComposeUiTest.setView( state: DesktopNoticeState, onBackClicked: () -> Unit = EnsureNeverCalled(), onReadyToScanClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorViewTest.kt index aa52a70149..b63d7471ac 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorViewTest.kt @@ -5,58 +5,56 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.error import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce 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 ErrorViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the onCancel callback`() { + fun `on back pressed - calls the onCancel callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setErrorView( + setErrorView( onCancel = callback, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on try again button clicked - calls the expected callback`() { + fun `on try again button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setErrorView( + setErrorView( onRetry = callback ) - rule.clickOn(CommonStrings.action_try_again) + clickOn(CommonStrings.action_try_again) } } @Test - fun `on cancel button clicked - calls the expected callback`() { + fun `on cancel button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setErrorView( + setErrorView( onCancel = callback ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) } } - private fun AndroidComposeTestRule.setErrorView( + private fun AndroidComposeUiTest.setErrorView( onRetry: () -> Unit = EnsureNeverCalled(), onCancel: () -> Unit = EnsureNeverCalled(), errorScreenType: ErrorScreenType = ErrorScreenType.UnknownError, diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberViewTest.kt index 20e1d898dd..25dc9efa8a 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/number/EnterNumberViewTest.kt @@ -5,13 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.number import androidx.activity.ComponentActivity +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsNotEnabled -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -20,65 +23,60 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 EnterNumberViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( state = aEnterNumberState(), onBackClicked = callback, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on back button clicked - calls the expected callback`() { + fun `on back button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( state = aEnterNumberState(), onBackClicked = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `on continue button clicked - emits the Continue event`() { + fun `on continue button clicked - emits the Continue event`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder() - rule.setView( + setView( state = aEnterNumberState( number = "12", eventSink = eventRecorder, ), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventRecorder.assertSingle(EnterNumberEvent.Continue) } @Test - fun `when the number is not complete, continue button is disabled`() { + fun `when the number is not complete, continue button is disabled`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( state = aEnterNumberState( number = "1", eventSink = eventRecorder, ), ) - val continueStr = rule.activity.getString(CommonStrings.action_continue) - rule.onNodeWithText(continueStr).assertIsNotEnabled() + val continueStr = activity!!.getString(CommonStrings.action_continue) + onNodeWithText(continueStr).assertIsNotEnabled() } - private fun AndroidComposeTestRule.setView( + private fun AndroidComposeUiTest.setView( state: EnterNumberState, onBackClicked: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt index c6c89ba818..d552c2bff6 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt @@ -5,36 +5,34 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.qrcode import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.ensureCalledOnce 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 ShowQrCodeViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setView( + setView( onBackClick = callback ) - rule.pressBackKey() + pressBackKey() } } - private fun AndroidComposeTestRule.setView( + private fun AndroidComposeUiTest.setView( onBackClick: () -> Unit = EnsureNeverCalled(), ) { setContent { diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootViewTest.kt index e352debfb0..bceb8753b2 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/root/LinkNewDeviceRootViewTest.kt @@ -5,11 +5,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.linknewdevice.impl.R import io.element.android.libraries.architecture.AsyncData @@ -19,74 +22,69 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBackKey -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LinkNewDeviceRootViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the onRetry callback`() { + fun `on back pressed - calls the onRetry callback`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLinkNewDeviceRootView( + setLinkNewDeviceRootView( state = aLinkNewDeviceRootState( eventSink = eventRecorder, ), onBackClick = callback ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `link desktop button clicked - calls the expected callback`() { + fun `link desktop button clicked - calls the expected callback`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLinkNewDeviceRootView( + setLinkNewDeviceRootView( state = aLinkNewDeviceRootState( isSupported = AsyncData.Success(true), eventSink = eventRecorder, ), onLinkDesktopDeviceClick = callback, ) - rule.clickOn(R.string.screen_link_new_device_root_desktop_computer) + clickOn(R.string.screen_link_new_device_root_desktop_computer) } } @Test - fun `link mobile button clicked - emits the expected event`() { + fun `link mobile button clicked - emits the expected event`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder() - rule.setLinkNewDeviceRootView( + setLinkNewDeviceRootView( state = aLinkNewDeviceRootState( isSupported = AsyncData.Success(true), eventSink = eventRecorder, ) ) - rule.clickOn(R.string.screen_link_new_device_root_mobile_device) + clickOn(R.string.screen_link_new_device_root_mobile_device) eventRecorder.assertSingle(LinkNewDeviceRootEvent.LinkMobileDevice) } @Test - fun `not supported - dismiss click - invokes the expected callback`() { + fun `not supported - dismiss click - invokes the expected callback`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLinkNewDeviceRootView( + setLinkNewDeviceRootView( state = aLinkNewDeviceRootState( isSupported = AsyncData.Success(false), eventSink = eventRecorder, ), onBackClick = callback, ) - rule.clickOn(CommonStrings.action_dismiss) + clickOn(CommonStrings.action_dismiss) } } - private fun AndroidComposeTestRule.setLinkNewDeviceRootView( + private fun AndroidComposeUiTest.setLinkNewDeviceRootView( state: LinkNewDeviceRootState = aLinkNewDeviceRootState(), onBackClick: () -> Unit = EnsureNeverCalled(), onLinkDesktopDeviceClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeViewTest.kt index fcc3afeb7d..1932718fef 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/scan/ScanQrCodeViewTest.kt @@ -5,11 +5,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.linknewdevice.impl.screens.scan import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.test.AN_EXCEPTION @@ -19,44 +22,39 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBackKey -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ScanQrCodeViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( state = aScanQrCodeState( eventSink = eventRecorder, ), onBackClick = callback ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `try again button clicked - emits the expected event`() { + fun `try again button clicked - emits the expected event`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder() - rule.setView( + setView( state = aScanQrCodeState( scanAction = AsyncAction.Failure(AN_EXCEPTION), eventSink = eventRecorder, ) ) - rule.clickOn(CommonStrings.action_try_again) + clickOn(CommonStrings.action_try_again) eventRecorder.assertSingle(ScanQrCodeEvent.TryAgain) } - private fun AndroidComposeTestRule.setView( + private fun AndroidComposeUiTest.setView( state: ScanQrCodeState = aScanQrCodeState(), onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt index 317fbf8fed..63c19ba913 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt @@ -5,15 +5,18 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.location.impl.share import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.libraries.testtags.TestTags @@ -23,102 +26,98 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ShareLocationViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `test back action`() { + fun `test back action`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setShareLocationView( + setShareLocationView( state = aShareLocationState( eventSink = eventsRecorder ), navigateUp = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `test fab click`() { + fun `test fab click`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), ) - rule.onNodeWithTag(TestTags.floatingActionButton.value).performClick() + onNodeWithTag(TestTags.floatingActionButton.value).performClick() eventsRecorder.assertSingle(ShareLocationEvent.StartTrackingUserLocation) } @Test - fun `when permission denied is displayed user can open the settings`() { + fun `when permission denied is displayed user can open the settings`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied), eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ShareLocationEvent.OpenAppSettings) } @Test - fun `when permission denied is displayed user can close the dialog`() { + fun `when permission denied is displayed user can close the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied), eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog) } @Test - fun `when permission rationale is displayed user can request permissions`() { + fun `when permission rationale is displayed user can request permissions`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale), eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ShareLocationEvent.RequestPermissions) } @Test - fun `when permission rationale is displayed user can close the dialog`() { + fun `when permission rationale is displayed user can close the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale), eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog) } @Test - fun `when location service disabled is displayed user can open location settings`() { + fun `when location service disabled is displayed user can open location settings`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled), hasLocationPermission = true, @@ -126,14 +125,14 @@ class ShareLocationViewTest { ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ShareLocationEvent.OpenLocationSettings) } @Test - fun `when location service disabled is displayed user can close the dialog`() { + fun `when location service disabled is displayed user can close the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShareLocationView( + setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled), hasLocationPermission = true, @@ -141,12 +140,12 @@ class ShareLocationViewTest { ), navigateUp = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog) } } -private fun AndroidComposeTestRule.setShareLocationView( +private fun AndroidComposeUiTest.setShareLocationView( state: ShareLocationState, navigateUp: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt index fecbbdbf89..45ed894f97 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationViewTest.kt @@ -6,16 +6,19 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.location.impl.show import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState @@ -26,115 +29,111 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ShowLocationViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `test back action`() { + fun `test back action`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setShowLocationView( + setShowLocationView( state = aShowLocationState( eventSink = eventsRecorder ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `test share action`() { + fun `test share action`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - val shareContentDescription = rule.activity.getString(CommonStrings.action_share) - rule.onNodeWithContentDescription(shareContentDescription).performClick() + val shareContentDescription = activity!!.getString(CommonStrings.action_share) + onNodeWithContentDescription(shareContentDescription).performClick() // The default aStaticLocationMode uses Location(1.23, 2.34, 4f) eventsRecorder.assertSingle(ShowLocationEvent.Share(Location(1.23, 2.34, 4f))) } @Test - fun `test fab click`() { + fun `test fab click`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - rule.onNodeWithTag(TestTags.floatingActionButton.value).performClick() + onNodeWithTag(TestTags.floatingActionButton.value).performClick() eventsRecorder.assertSingle(ShowLocationEvent.TrackMyLocation(true)) } @Test - fun `when permission denied is displayed user can open the settings`() { + fun `when permission denied is displayed user can open the settings`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( constraintsDialogState = LocationConstraintsDialogState.PermissionDenied, eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ShowLocationEvent.OpenAppSettings) } @Test - fun `when permission denied is displayed user can close the dialog`() { + fun `when permission denied is displayed user can close the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( constraintsDialogState = LocationConstraintsDialogState.PermissionDenied, eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShowLocationEvent.DismissDialog) } @Test - fun `when permission rationale is displayed user can request permissions`() { + fun `when permission rationale is displayed user can request permissions`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( constraintsDialogState = LocationConstraintsDialogState.PermissionRationale, eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ShowLocationEvent.RequestPermissions) } @Test - fun `when permission rationale is displayed user can close the dialog`() { + fun `when permission rationale is displayed user can close the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setShowLocationView( + setShowLocationView( aShowLocationState( constraintsDialogState = LocationConstraintsDialogState.PermissionRationale, eventSink = eventsRecorder ), onBackClick = EnsureNeverCalled(), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShowLocationEvent.DismissDialog) } } -private fun AndroidComposeTestRule.setShowLocationView( +private fun AndroidComposeUiTest.setShowLocationView( state: ShowLocationState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadTest.kt index 1ecb79bd67..e6d1659778 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/keypad/PinKeypadTest.kt @@ -6,60 +6,57 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.lockscreen.impl.unlock.keypad import android.view.KeyEvent import androidx.activity.ComponentActivity import androidx.compose.ui.input.key.Key +import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isRoot -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performKeyInput import androidx.compose.ui.test.pressKey import androidx.compose.ui.test.requestFocus +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.compose.ui.unit.dp import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class PinKeypadTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on a number emits the expected event`() { + fun `clicking on a number emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPinKeyPad(onClick = eventsRecorder) - rule.onNode(hasText("1")).performClick() + setPinKeyPad(onClick = eventsRecorder) + onNode(hasText("1")).performClick() eventsRecorder.assertSingle(PinKeypadModel.Number('1')) } @Test - fun `clicking on the delete previous character button emits the expected event`() { + fun `clicking on the delete previous character button emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPinKeyPad(onClick = eventsRecorder) - rule.onNode(hasContentDescription(rule.activity.getString(CommonStrings.a11y_delete))).performClick() + setPinKeyPad(onClick = eventsRecorder) + onNode(hasContentDescription(activity!!.getString(CommonStrings.a11y_delete))).performClick() eventsRecorder.assertSingle(PinKeypadModel.Back) } @OptIn(ExperimentalTestApi::class) @Test - fun `typing using the hardware keyboard emits the expected events`() { + fun `typing using the hardware keyboard emits the expected events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPinKeyPad(onClick = eventsRecorder) - rule.onNodeWithText("1").requestFocus() - rule.onAllNodes(isRoot())[0].performKeyInput { + setPinKeyPad(onClick = eventsRecorder) + onNodeWithText("1").requestFocus() + onAllNodes(isRoot())[0].performKeyInput { val keys = listOf( Key.A, Key.NumPad1, @@ -118,7 +115,7 @@ class PinKeypadTest { ) } - private fun AndroidComposeTestRule.setPinKeyPad( + private fun AndroidComposeUiTest.setPinKeyPad( onClick: (PinKeypadModel) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt index c6610b212c..61ec7cc698 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.chooseaccountprovider import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.login.impl.accountprovider.anAccountProvider import io.element.android.libraries.architecture.AsyncData @@ -25,36 +28,31 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class ChooseAccountProviderViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setChooseAccountProviderView( + setChooseAccountProviderView( state = aChooseAccountProviderState( eventSink = eventSink, ), onBackClick = it, ) - rule.pressBack() + pressBack() } } @Config(qualifiers = "h1024dp") @Test - fun `selecting an account provider emits the the expected event`() { + fun `selecting an account provider emits the the expected event`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() - rule.setChooseAccountProviderView( + setChooseAccountProviderView( state = aChooseAccountProviderState( accountProviders = listOf( ChooseAccountProviderPresenterTest.accountProvider1, @@ -64,24 +62,24 @@ class ChooseAccountProviderViewTest { eventSink = eventSink, ), ) - rule.onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick() + onNodeWithText(ChooseAccountProviderPresenterTest.accountProvider1.title).performClick() eventSink.assertSingle(ChooseAccountProviderEvents.SelectAccountProvider(ChooseAccountProviderPresenterTest.accountProvider1)) } @Test - fun `when error is displayed - closing the dialog emits the expected event`() { + fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() - rule.setChooseAccountProviderView( + setChooseAccountProviderView( state = aChooseAccountProviderState( loginMode = AsyncData.Failure(AN_EXCEPTION), eventSink = eventSink, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventSink.assertSingle(ChooseAccountProviderEvents.ClearError) } - private fun AndroidComposeTestRule.setChooseAccountProviderView( + private fun AndroidComposeUiTest.setChooseAccountProviderView( state: ChooseAccountProviderState, onBackClick: () -> Unit = EnsureNeverCalled(), onOAuthDetails: (OAuthDetails) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt index 26da50da63..c0e7e5c378 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordViewTest.kt @@ -6,20 +6,23 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.loginpassword import androidx.activity.ComponentActivity +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_USER_NAME @@ -30,158 +33,154 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class LoginPasswordViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke back callback`() { + fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `changing login invokes the expected event`() { + fun `changing login invokes the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, ), ) - val userNameHint = rule.activity.getString(CommonStrings.common_username) - rule.onNodeWithText(userNameHint).performTextInput(A_USER_NAME) + val userNameHint = activity!!.getString(CommonStrings.common_username) + onNodeWithText(userNameHint).performTextInput(A_USER_NAME) eventsRecorder.assertSingle( LoginPasswordEvents.SetLogin(A_USER_NAME) ) } @Test - fun `changing login removes new lines the expected event`() { + fun `changing login removes new lines the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, ), ) - val userNameHint = rule.activity.getString(CommonStrings.common_username) - rule.onNodeWithText(userNameHint).performTextInput("a\nb") + val userNameHint = activity!!.getString(CommonStrings.common_username) + onNodeWithText(userNameHint).performTextInput("a\nb") eventsRecorder.assertSingle( LoginPasswordEvents.SetLogin("ab") ) } @Test - fun `clearing login invokes the expected event`() { + fun `clearing login invokes the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(A_USER_NAME), eventSink = eventsRecorder, ), ) - val a11yClear = rule.activity.getString(CommonStrings.action_clear) - rule.onNodeWithContentDescription(a11yClear).performClick() + val a11yClear = activity!!.getString(CommonStrings.action_clear) + onNodeWithContentDescription(a11yClear).performClick() eventsRecorder.assertSingle( LoginPasswordEvents.SetLogin("") ) } @Test - fun `changing password invokes the expected event`() { + fun `changing password invokes the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( eventSink = eventsRecorder, ), ) - val userNameHint = rule.activity.getString(CommonStrings.common_password) - rule.onNodeWithText(userNameHint).performTextInput(A_PASSWORD) + val userNameHint = activity!!.getString(CommonStrings.common_password) + onNodeWithText(userNameHint).performTextInput(A_PASSWORD) eventsRecorder.assertSingle( LoginPasswordEvents.SetPassword(A_PASSWORD) ) } @Test - fun `reveal password makes the password visible`() { + fun `reveal password makes the password visible`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(password = A_PASSWORD), eventSink = eventsRecorder, ), ) - rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••")) + onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••")) + val resources = activity!!.resources // Show password - val a11yShowPassword = rule.activity.getString(CommonStrings.a11y_show_password) - rule.onNodeWithContentDescription(a11yShowPassword).performClick() - rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText(A_PASSWORD)) + val a11yShowPassword = resources.getString(CommonStrings.a11y_show_password) + onNodeWithContentDescription(a11yShowPassword).performClick() + onNodeWithTag(TestTags.loginPassword.value).assert(hasText(A_PASSWORD)) // Hide password - val a11yHidePassword = rule.activity.getString(CommonStrings.a11y_hide_password) - rule.onNodeWithContentDescription(a11yHidePassword).performClick() - rule.onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••")) + val a11yHidePassword = resources.getString(CommonStrings.a11y_hide_password) + onNodeWithContentDescription(a11yHidePassword).performClick() + onNodeWithTag(TestTags.loginPassword.value).assert(hasText("••••••••")) } @Test - fun `when login is empty, continue button is not enabled`() { + fun `when login is empty, continue button is not enabled`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(password = A_PASSWORD), eventSink = eventsRecorder, ), ) - val continueStr = rule.activity.getString(CommonStrings.action_continue) - rule.onNodeWithText(continueStr).assertIsNotEnabled() + val continueStr = activity!!.getString(CommonStrings.action_continue) + onNodeWithText(continueStr).assertIsNotEnabled() } @Test - fun `when password is empty, continue button is not enabled`() { + fun `when password is empty, continue button is not enabled`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(login = A_USER_NAME), eventSink = eventsRecorder, ), ) - val continueStr = rule.activity.getString(CommonStrings.action_continue) - rule.onNodeWithText(continueStr).assertIsNotEnabled() + val continueStr = activity!!.getString(CommonStrings.action_continue) + onNodeWithText(continueStr).assertIsNotEnabled() } @Config(qualifiers = "h1024dp") @Test - fun `clicking on Continue sends expected event`() { + fun `clicking on Continue sends expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLoginPasswordView( + setLoginPasswordView( aLoginPasswordState( formState = aLoginFormState(login = A_USER_NAME, password = A_PASSWORD), eventSink = eventsRecorder, ), ) - val continueStr = rule.activity.getString(CommonStrings.action_continue) - rule.onNodeWithText(continueStr).assertIsEnabled() - rule.clickOn(CommonStrings.action_continue) + val continueStr = activity!!.getString(CommonStrings.action_continue) + onNodeWithText(continueStr).assertIsEnabled() + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle( LoginPasswordEvents.Submit ) } } -private fun AndroidComposeTestRule.setLoginPasswordView( +private fun AndroidComposeUiTest.setLoginPasswordView( state: LoginPasswordState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt index a8f0ccbb5a..bcb62ea707 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/onboarding/OnboardingViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.onboarding import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import com.google.testing.junit.testparameterinjector.KotlinTestParameters.namedTestValues import com.google.testing.junit.testparameterinjector.TestParameter import io.element.android.features.login.impl.R @@ -29,22 +32,17 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.RobolectricTestParameterInjector @RunWith(RobolectricTestParameterInjector::class) class OnboardingViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `when can create account - clicking on create account calls the expected callback`() { + fun `when can create account - clicking on create account calls the expected callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canCreateAccount = true, showDeveloperSettings = false, @@ -52,40 +50,40 @@ class OnboardingViewTest { ), onCreateAccount = callback, ) - rule.clickOn(R.string.screen_onboarding_sign_up) + clickOn(R.string.screen_onboarding_sign_up) // Developer settings should not be shown - val developerSettingsText = rule.activity.getString(CommonStrings.common_developer_options) - rule.onNodeWithContentDescription(developerSettingsText).assertDoesNotExist() + val developerSettingsText = activity!!.getString(CommonStrings.common_developer_options) + onNodeWithContentDescription(developerSettingsText).assertDoesNotExist() } } @Test - fun `when can go back - clicking on back calls the expected callback`() { + fun `when can go back - clicking on back calls the expected callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( isAddingAccount = true, eventSink = eventSink, ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() { + fun `when can login with QR code - clicking on sign in with QR code calls the expected callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canLoginWithQrCode = true, eventSink = eventSink, ), onSignInWithQrCode = callback, ) - rule.clickOn(R.string.screen_onboarding_sign_in_with_qr_code) + clickOn(R.string.screen_onboarding_sign_in_with_qr_code) } } @@ -95,10 +93,10 @@ class OnboardingViewTest { "can search account provider" to false, "cannot search account provider" to true, ) - ) { + ) = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(mustChooseAccountProvider) { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canLoginWithQrCode = true, mustChooseAccountProvider = mustChooseAccountProvider, @@ -106,7 +104,7 @@ class OnboardingViewTest { ), onSignIn = callback, ) - rule.clickOn(R.string.screen_onboarding_sign_in_manually) + clickOn(R.string.screen_onboarding_sign_in_manually) } } @@ -116,10 +114,10 @@ class OnboardingViewTest { "can search account provider" to false, "cannot search account provider" to true, ) - ) { + ) = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(mustChooseAccountProvider) { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canLoginWithQrCode = false, canCreateAccount = false, @@ -128,89 +126,89 @@ class OnboardingViewTest { ), onSignIn = callback, ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) } } @Test - fun `when sign in to pre defined account provider - clicking on button emits the expected event`() { + fun `when sign in to pre defined account provider - clicking on button emits the expected event`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( defaultAccountProvider = "element.io", eventSink = eventSink, ), ) - val buttonText = rule.activity.getString(R.string.screen_onboarding_sign_in_to, "element.io") - rule.onNodeWithText(buttonText).performClick() + val buttonText = activity!!.getString(R.string.screen_onboarding_sign_in_to, "element.io") + onNodeWithText(buttonText).performClick() eventSink.assertSingle(OnBoardingEvents.OnSignIn("element.io")) } @Test - fun `when error is displayed - closing the dialog emits the expected event`() { + fun `when error is displayed - closing the dialog emits the expected event`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( defaultAccountProvider = "element.io", loginMode = AsyncData.Failure(AN_EXCEPTION), eventSink = eventSink, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventSink.assertSingle(OnBoardingEvents.ClearError) } @Test - fun `clicking on report a problem calls the sign in callback`() { + fun `clicking on report a problem calls the sign in callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canReportBug = true, eventSink = eventSink, ), onReportProblem = callback, ) - val text = rule.activity.getString(CommonStrings.common_report_a_problem) - rule.onNodeWithText(text).assertExists() - rule.clickOn(CommonStrings.common_report_a_problem) + val text = activity!!.getString(CommonStrings.common_report_a_problem) + onNodeWithText(text).assertExists() + clickOn(CommonStrings.common_report_a_problem) } } @Test - fun `clicking on settings calls the developer settings callback`() { + fun `clicking on settings calls the developer settings callback`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( showDeveloperSettings = true, eventSink = eventSink, ), onDeveloperSettingsClick = callback, ) - val text = rule.activity.getString(CommonStrings.common_developer_options) - rule.onNodeWithContentDescription(text).performClick() + val text = activity!!.getString(CommonStrings.common_developer_options) + onNodeWithContentDescription(text).performClick() } } @Test - fun `cannot report a problem when the feature is disabled`() { + fun `cannot report a problem when the feature is disabled`() = runAndroidComposeUiTest { val eventSink = EventsRecorder(expectEvents = false) - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( canReportBug = false, eventSink = eventSink, ), ) - val text = rule.activity.getString(CommonStrings.common_report_a_problem) - rule.onNodeWithText(text).assertDoesNotExist() + val text = activity!!.getString(CommonStrings.common_report_a_problem) + onNodeWithText(text).assertDoesNotExist() } @Test - fun `when success PasswordLogin - the expected callback is invoked and the event is received`() { + fun `when success PasswordLogin - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() ensureCalledOnce { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( loginMode = AsyncData.Success(LoginMode.PasswordLogin), eventSink = eventSink, @@ -222,11 +220,11 @@ class OnboardingViewTest { } @Test - fun `when success Oidc - the expected callback is invoked and the event is received`() { + fun `when success Oidc - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() val oAuthDetails = OAuthDetails("aUrl") ensureCalledOnceWithParam(oAuthDetails) { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( loginMode = AsyncData.Success(LoginMode.OAuth(oAuthDetails)), eventSink = eventSink, @@ -238,11 +236,11 @@ class OnboardingViewTest { } @Test - fun `when success AccountCreation - the expected callback is invoked and the event is received`() { + fun `when success AccountCreation - the expected callback is invoked and the event is received`() = runAndroidComposeUiTest { val eventSink = EventsRecorder() val oAuthDetails = OAuthDetails("aUrl") ensureCalledOnceWithParam(oAuthDetails.url) { callback -> - rule.setOnboardingView( + setOnboardingView( state = anOnBoardingState( loginMode = AsyncData.Success(LoginMode.AccountCreation("aUrl")), eventSink = eventSink, @@ -253,7 +251,7 @@ class OnboardingViewTest { eventSink.assertSingle(OnBoardingEvents.ClearError) } - private fun AndroidComposeTestRule.setOnboardingView( + private fun AndroidComposeUiTest.setOnboardingView( state: OnBoardingState, onBackClick: () -> Unit = EnsureNeverCalled(), onDeveloperSettingsClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationViewTest.kt index a0469a684e..79566625c5 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationViewTest.kt @@ -6,49 +6,47 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.qrcode.confirmation import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce 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 QrCodeConfirmationViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeConfirmationView( + setQrCodeConfirmationView( step = QrCodeConfirmationStep.DisplayCheckCode("12"), onCancel = callback ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on Cancel button clicked - calls the expected callback`() { + fun `on Cancel button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeConfirmationView( + setQrCodeConfirmationView( step = QrCodeConfirmationStep.DisplayVerificationCode("123456"), onCancel = callback ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) } } - private fun AndroidComposeTestRule.setQrCodeConfirmationView( + private fun AndroidComposeUiTest.setQrCodeConfirmationView( step: QrCodeConfirmationStep, onCancel: () -> Unit ) { diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorViewTest.kt index de0f689220..2ae68c3485 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.qrcode.error import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType import io.element.android.libraries.ui.strings.CommonStrings @@ -18,47 +21,42 @@ import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce 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 QrCodeErrorViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the onCancel callback`() { + fun `on back pressed - calls the onCancel callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeErrorView( + setQrCodeErrorView( onCancel = callback, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on try again button clicked - calls the expected callback`() { + fun `on try again button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeErrorView( + setQrCodeErrorView( onRetry = callback, ) - rule.clickOn(CommonStrings.action_try_again) + clickOn(CommonStrings.action_try_again) } } @Test - fun `on cancel button clicked - calls the expected callback`() { + fun `on cancel button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeErrorView( + setQrCodeErrorView( onCancel = callback, ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) } } - private fun AndroidComposeTestRule.setQrCodeErrorView( + private fun AndroidComposeUiTest.setQrCodeErrorView( onRetry: () -> Unit = EnsureNeverCalled(), onCancel: () -> Unit = EnsureNeverCalled(), errorScreenType: QrCodeErrorScreenType = QrCodeErrorScreenType.UnknownError, diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt index cec67e5011..c6812d3759 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.qrcode.intro import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.login.impl.R import io.element.android.tests.testutils.EnsureNeverCalled @@ -19,42 +22,37 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 QrCodeIntroViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeIntroView( + setQrCodeIntroView( state = aQrCodeIntroState(), onBackClicked = callback ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on back button clicked - calls the expected callback`() { + fun `on back button clicked - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeIntroView( + setQrCodeIntroView( state = aQrCodeIntroState(), onBackClicked = callback ) - rule.pressBack() + pressBack() } } @Test - fun `when can continue - calls the expected callback`() { + fun `when can continue - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeIntroView( + setQrCodeIntroView( state = aQrCodeIntroState(canContinue = true), onContinue = callback ) @@ -62,16 +60,16 @@ class QrCodeIntroViewTest { } @Test - fun `on submit button clicked - emits the Continue event`() { + fun `on submit button clicked - emits the Continue event`() = runAndroidComposeUiTest { val eventRecorder = EventsRecorder() - rule.setQrCodeIntroView( + setQrCodeIntroView( state = aQrCodeIntroState(eventSink = eventRecorder), ) - rule.clickOn(R.string.screen_qr_code_login_initial_state_button_title) + clickOn(R.string.screen_qr_code_login_initial_state_button_title) eventRecorder.assertSingle(QrCodeIntroEvents.Continue) } - private fun AndroidComposeTestRule.setQrCodeIntroView( + private fun AndroidComposeUiTest.setQrCodeIntroView( state: QrCodeIntroState, onBackClicked: () -> Unit = EnsureNeverCalled(), onContinue: () -> Unit = EnsureNeverCalled(), diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanViewTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanViewTest.kt index b8becd545f..bde960ef1a 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanViewTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanViewTest.kt @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.login.impl.screens.qrcode.scan import androidx.activity.ComponentActivity import androidx.camera.lifecycle.ProcessCameraProvider -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import io.element.android.libraries.architecture.AsyncAction @@ -24,16 +27,11 @@ import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBackKey import org.junit.After import org.junit.Before -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class QrCodeScanViewTest { - @get:Rule - val rule = createAndroidComposeRule() - private var provider: ProcessCameraProvider? = null @Before @@ -48,28 +46,28 @@ class QrCodeScanViewTest { } @Test - fun `on back pressed - calls the expected callback`() { + fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setQrCodeScanView( + setQrCodeScanView( state = aQrCodeScanState(), onBackClick = callback ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `on QR code data ready - calls the expected callback`() { + fun `on QR code data ready - calls the expected callback`() = runAndroidComposeUiTest { val data = FakeMatrixQrCodeLoginData() ensureCalledOnceWithParam(data) { callback -> - rule.setQrCodeScanView( + setQrCodeScanView( state = aQrCodeScanState(authenticationAction = AsyncAction.Success(data)), onQrCodeDataReady = callback ) } } - private fun AndroidComposeTestRule.setQrCodeScanView( + private fun AndroidComposeUiTest.setQrCodeScanView( state: QrCodeScanState, onBackClick: () -> Unit = EnsureNeverCalled(), onQrCodeDataReady: (MatrixQrCodeLoginData) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt index 84ca038d7b..a42fd891d4 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.logout.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.testtags.TestTags @@ -21,97 +24,93 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressTag -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LogoutViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on logout sends a LogoutEvents`() { + fun `clicking on logout sends a LogoutEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLogoutView( + setLogoutView( aLogoutState( eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_signout) + clickOn(CommonStrings.action_signout) eventsRecorder.assertSingle(LogoutEvents.Logout(false)) } @Test - fun `confirming logout sends a LogoutEvents`() { + fun `confirming logout sends a LogoutEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLogoutView( + setLogoutView( aLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(LogoutEvents.Logout(false)) } @Test - fun `clicking on back invoke back callback`() { + fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLogoutView( + setLogoutView( aLogoutState( eventSink = eventsRecorder ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on confirm after error sends a LogoutEvents`() { + fun `clicking on confirm after error sends a LogoutEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLogoutView( + setLogoutView( aLogoutState( logoutAction = AsyncAction.Failure(Exception("Failed to logout")), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_signout_anyway) + clickOn(CommonStrings.action_signout_anyway) eventsRecorder.assertSingle(LogoutEvents.Logout(true)) } @Test - fun `clicking on cancel after error sends a LogoutEvents`() { + fun `clicking on cancel after error sends a LogoutEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLogoutView( + setLogoutView( aLogoutState( logoutAction = AsyncAction.Failure(Exception("Failed to logout")), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(LogoutEvents.CloseDialogs) } @Test - fun `last session setting button invoke onChangeRecoveryKeyClicked`() { + fun `last session setting button invoke onChangeRecoveryKeyClicked`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setLogoutView( + setLogoutView( aLogoutState( isLastDevice = true, eventSink = eventsRecorder ), onChangeRecoveryKeyClick = callback, ) - rule.clickOn(CommonStrings.common_settings) + clickOn(CommonStrings.common_settings) } } } -private fun AndroidComposeTestRule.setLogoutView( +private fun AndroidComposeUiTest.setLogoutView( state: LogoutState, onChangeRecoveryKeyClick: () -> Unit = EnsureNeverCalled(), onBackClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt index 8eae534740..99860259c4 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/direct/DefaultDirectLogoutViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.logout.impl.direct import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutState @@ -21,83 +24,79 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressBackKey import org.junit.Ignore -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class DefaultDirectLogoutViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on confirm logout sends expected Event`() { + fun `clicking on confirm logout sends expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDefaultDirectLogoutView( + setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_signout) + clickOn(CommonStrings.action_signout) eventsRecorder.assertSingle(DirectLogoutEvents.Logout(false)) } @Test - fun `clicking on cancel logout sends expected Event`() { + fun `clicking on cancel logout sends expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDefaultDirectLogoutView( + setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) } @Ignore("Pressing back key should dismiss the dialog, and so generate the expected event, but it's not the case.") @Test - fun `clicking on back invoke back callback`() { + fun `clicking on back invoke back callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDefaultDirectLogoutView( + setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder, ) ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) } @Test - fun `clicking on confirm after error sends expected Event`() { + fun `clicking on confirm after error sends expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDefaultDirectLogoutView( + setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.Failure(Exception("Error")), eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_signout_anyway) + clickOn(CommonStrings.action_signout_anyway) eventsRecorder.assertSingle(DirectLogoutEvents.Logout(true)) } @Test - fun `clicking on cancel after error sends expected Event`() { + fun `clicking on cancel after error sends expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDefaultDirectLogoutView( + setDefaultDirectLogoutView( state = aDirectLogoutState( logoutAction = AsyncAction.Failure(Exception("Error")), eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(DirectLogoutEvents.CloseDialogs) } } -private fun AndroidComposeTestRule.setDefaultDirectLogoutView( +private fun AndroidComposeUiTest.setDefaultDirectLogoutView( state: DirectLogoutState, ) { setContent { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index 62b9eac68d..70ef70325e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -6,13 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.longClick import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onAllNodesWithTag @@ -25,6 +27,7 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeRight +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.emojibasebindings.Emoji @@ -78,82 +81,78 @@ import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.setSafeContent import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config import kotlin.time.Duration.Companion.milliseconds @RunWith(AndroidJUnit4::class) class MessagesViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke expected callback`() { + fun `clicking on back invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMessagesView( + setMessagesView( state = state, onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on room name invoke expected callback`() { + fun `clicking on room name invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMessagesView( + setMessagesView( state = state, onRoomDetailsClick = callback, ) - rule.onNodeWithText(state.roomName.orEmpty(), useUnmergedTree = true).performClick() + onNodeWithText(state.roomName.orEmpty(), useUnmergedTree = true).performClick() } } @Test - fun `clicking on join call invoke expected callback`() { + fun `clicking on join call invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( eventSink = eventsRecorder ) ensureCalledOnceWithParam(false) { callback -> - rule.setMessagesView( + setMessagesView( state = state, onJoinCallClick = callback, ) - val joinCallContentDescription = rule.activity.getString(CommonStrings.a11y_start_call) - rule.onNodeWithContentDescription(joinCallContentDescription).performClick() + val joinCallContentDescription = activity!!.getString(CommonStrings.a11y_start_call) + onNodeWithContentDescription(joinCallContentDescription).performClick() } } @Test - fun `clicking on join voice call invoke expected callback`() { + fun `clicking on join voice call invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( eventSink = eventsRecorder, roomCallState = aStandByCallState(isDM = true) ) ensureCalledOnceWithParam(true) { callback -> - rule.setMessagesView( + setMessagesView( state = state, onJoinCallClick = callback, ) - val joinVoiceCallContentDescription = rule.activity.getString(CommonStrings.a11y_start_voice_call) - rule.onNodeWithContentDescription(joinVoiceCallContentDescription).performClick() + val joinVoiceCallContentDescription = activity!!.getString(CommonStrings.a11y_start_voice_call) + onNodeWithContentDescription(joinVoiceCallContentDescription).performClick() } } @Test - fun `clicking on an Event invoke expected callback`() { + fun `clicking on an Event invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( timelineState = aTimelineState( @@ -167,12 +166,12 @@ class MessagesViewTest { expectedParam2 = timelineItem, result = true, ) - rule.setMessagesView( + setMessagesView( state = state, onEventClick = callback, ) // Cannot perform click on "Text", it's not detected. Use tag instead - rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performClick() + onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performClick() callback.assertSuccess() } @@ -202,7 +201,7 @@ class MessagesViewTest { userHasPermissionToRedactOther: Boolean = false, userHasPermissionToSendReaction: Boolean = false, userCanPinEvent: Boolean = false, - ) { + ) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( actionListState = anActionListState( @@ -220,11 +219,11 @@ class MessagesViewTest { ), ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event - rule.setMessagesView( + setMessagesView( state = state, ) // Cannot perform click on "Text", it's not detected. Use tag instead - rule.onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { longClick() } + onAllNodesWithTag(TestTags.messageBubble.value).onFirst().performTouchInput { longClick() } eventsRecorder.assertSingle( ActionListEvent.ComputeForMessage( event = timelineItem, @@ -235,7 +234,7 @@ class MessagesViewTest { @Test @Config(qualifiers = "h1024dp") - fun `clicking on a read receipt list emits the expected Event`() { + fun `clicking on a read receipt list emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( @@ -255,10 +254,10 @@ class MessagesViewTest { ), ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event - rule.setMessagesView( + setMessagesView( state = state, ) - rule.onNodeWithTag(TestTags.messageReadReceipts.value, useUnmergedTree = true).performClick() + onNodeWithTag(TestTags.messageReadReceipts.value, useUnmergedTree = true).performClick() eventsRecorder.assertSingle(ReadReceiptBottomSheetEvent.EventSelected(timelineItem)) } @@ -272,7 +271,7 @@ class MessagesViewTest { swipeTest(userHasPermissionToSendMessage = false) } - private fun swipeTest(userHasPermissionToSendMessage: Boolean) { + private fun swipeTest(userHasPermissionToSendMessage: Boolean) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val canBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = true) val cannotBeRepliedEvent = aTimelineItemEvent(canBeRepliedTo = false) @@ -285,10 +284,10 @@ class MessagesViewTest { ), eventSink = eventsRecorder, ) - rule.setMessagesView( + setMessagesView( state = state, ) - rule.onAllNodesWithTag(TestTags.messageBubble.value).apply { + onAllNodesWithTag(TestTags.messageBubble.value).apply { onFirst().performTouchInput { swipeRight(endX = 200f) } onLast().performTouchInput { swipeRight(endX = 200f) } } @@ -300,7 +299,7 @@ class MessagesViewTest { } @Test - fun `clicking on send location invoke expected callback`() { + fun `clicking on send location invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( composerState = aMessageComposerState( @@ -309,16 +308,16 @@ class MessagesViewTest { eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMessagesView( + setMessagesView( state = state, onSendLocationClick = callback, ) - rule.clickOn(R.string.screen_room_attachment_source_location) + clickOn(R.string.screen_room_attachment_source_location) } } @Test - fun `clicking on create poll invoke expected callback`() { + fun `clicking on create poll invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( composerState = aMessageComposerState( @@ -327,25 +326,25 @@ class MessagesViewTest { eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMessagesView( + setMessagesView( state = state, onCreatePollClick = callback, ) // Then click on the poll action - rule.clickOn(R.string.screen_room_attachment_source_poll) + clickOn(R.string.screen_room_attachment_source_poll) } } @Test @Config(qualifiers = "h1024dp") - fun `clicking on the avatar of the sender of an Event emits the expected event`() { + fun `clicking on the avatar of the sender of an Event emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( eventSink = eventsRecorder ) val timelineEvent = state.timelineState.timelineItems.filterIsInstance().first() - rule.setMessagesView(state = state) - rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick() + setMessagesView(state = state) + onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick() eventsRecorder.assertSingle( MessagesEvent.OnUserClicked( MatrixUser( @@ -359,12 +358,12 @@ class MessagesViewTest { @Test @Config(qualifiers = "h1024dp") - fun `clicking on the display name of the sender of an Event emits expected event`() { + fun `clicking on the display name of the sender of an Event emits expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState(eventSink = eventsRecorder) val timelineEvent = state.timelineState.timelineItems.filterIsInstance().first() - rule.setMessagesView(state = state) - rule.onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick() + setMessagesView(state = state) + onNodeWithTag(TestTags.timelineItemSenderAvatar.value, useUnmergedTree = true).performClick() eventsRecorder.assertSingle( MessagesEvent.OnUserClicked( MatrixUser( @@ -377,7 +376,7 @@ class MessagesViewTest { } @Test - fun `selecting a action on a message emits the expected Event`() { + fun `selecting a action on a message emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( eventSink = eventsRecorder @@ -395,17 +394,17 @@ class MessagesViewTest { ) ), ) - rule.setMessagesView( + setMessagesView( state = stateWithMessageAction, ) - rule.clickOn(CommonStrings.action_edit) + clickOn(CommonStrings.action_edit) // Give time for the close animation to complete - rule.mainClock.advanceTimeBy(milliseconds = 1_000) + mainClock.advanceTimeBy(milliseconds = 1_000) eventsRecorder.assertSingle(MessagesEvent.HandleAction(TimelineItemAction.Edit, timelineItem)) } @Test - fun `clicking on a reaction emits the expected Event`() { + fun `clicking on a reaction emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( @@ -414,10 +413,10 @@ class MessagesViewTest { eventSink = eventsRecorder, ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event - rule.setMessagesView( + setMessagesView( state = state, ) - rule.onAllNodesWithText( + onAllNodesWithText( text = "👍️", useUnmergedTree = true, ).onFirst().performClick() @@ -425,7 +424,7 @@ class MessagesViewTest { } @Test - fun `long clicking on a reaction emits the expected Event`() { + fun `long clicking on a reaction emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( @@ -437,10 +436,10 @@ class MessagesViewTest { ), ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event - rule.setMessagesView( + setMessagesView( state = state, ) - rule.onAllNodesWithText( + onAllNodesWithText( text = "👍️", useUnmergedTree = true, ).onFirst().performTouchInput { longClick() } @@ -448,7 +447,7 @@ class MessagesViewTest { } @Test - fun `clicking on more reaction emits the expected Event`() { + fun `clicking on more reaction emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( @@ -459,16 +458,16 @@ class MessagesViewTest { ), ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event - rule.setMessagesView( + setMessagesView( state = state, ) - val moreReactionContentDescription = rule.activity.getString(R.string.screen_room_timeline_add_reaction) - rule.onAllNodesWithContentDescription(moreReactionContentDescription).onFirst().performClick() + val moreReactionContentDescription = activity!!.getString(R.string.screen_room_timeline_add_reaction) + onAllNodesWithContentDescription(moreReactionContentDescription).onFirst().performClick() eventsRecorder.assertSingle(CustomReactionEvent.ShowCustomReactionSheet(timelineItem)) } @Test - fun `clicking on more reaction from action list emits the expected Event`() { + fun `clicking on more reaction from action list emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( @@ -491,18 +490,18 @@ class MessagesViewTest { eventSink = eventsRecorder ), ) - rule.setMessagesView( + setMessagesView( state = stateWithActionListState, ) - val moreReactionContentDescription = rule.activity.getString(CommonStrings.a11y_react_with_other_emojis) - rule.onNodeWithContentDescription(moreReactionContentDescription).performClick() + val moreReactionContentDescription = activity!!.getString(CommonStrings.a11y_react_with_other_emojis) + onNodeWithContentDescription(moreReactionContentDescription).performClick() // Give time for the close animation to complete - rule.mainClock.advanceTimeBy(milliseconds = 1_000) + mainClock.advanceTimeBy(milliseconds = 1_000) eventsRecorder.assertSingle(CustomReactionEvent.ShowCustomReactionSheet(timelineItem)) } @Test - fun `clicking on verified user send failure from action list emits the expected Event`() { + fun `clicking on verified user send failure from action list emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState() val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event @@ -519,21 +518,21 @@ class MessagesViewTest { ), timelineState = aTimelineState(eventSink = eventsRecorder) ) - rule.setMessagesView( + setMessagesView( state = stateWithActionListState, ) // Clear initial 'LoadMore' event emitted when setting the state eventsRecorder.clear() - val verifiedUserSendFailure = rule.activity.getString(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, "Alice") - rule.onNodeWithText(verifiedUserSendFailure).performClick() + val verifiedUserSendFailure = activity!!.getString(CommonStrings.screen_timeline_item_menu_send_failure_changed_identity, "Alice") + onNodeWithText(verifiedUserSendFailure).performClick() // Give time for the close animation to complete - rule.mainClock.advanceTimeBy(milliseconds = 1_000) + mainClock.advanceTimeBy(milliseconds = 1_000) eventsRecorder.assertSingle(TimelineEvent.ComputeVerifiedUserSendFailure(timelineItem)) } @Test - fun `clicking on a custom emoji emits the expected Events`() { + fun `clicking on a custom emoji emits the expected Events`() = runAndroidComposeUiTest { val aUnicode = "🙈" val customReactionStateEventsRecorder = EventsRecorder() val eventsRecorder = EventsRecorder() @@ -563,18 +562,18 @@ class MessagesViewTest { eventSink = customReactionStateEventsRecorder ), ) - rule.setMessagesView( + setMessagesView( state = stateWithCustomReactionState, ) - rule.onNodeWithText(aUnicode, useUnmergedTree = true).performClick() + onNodeWithText(aUnicode, useUnmergedTree = true).performClick() // Give time for the close animation to complete - rule.mainClock.advanceTimeBy(milliseconds = 1_000) + mainClock.advanceTimeBy(milliseconds = 1_000) customReactionStateEventsRecorder.assertSingle(CustomReactionEvent.DismissCustomReactionSheet) eventsRecorder.assertSingle(MessagesEvent.ToggleReaction(aUnicode, timelineItem.eventOrTransactionId)) } @Test - fun `clicking on pinned messages banner emits the expected Event`() { + fun `clicking on pinned messages banner emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState(eventSink = eventsRecorder), @@ -587,16 +586,16 @@ class MessagesViewTest { ), ), ) - rule.setMessagesView(state = state) + setMessagesView(state = state) // Clear initial 'LoadMore' event emitted when setting the state eventsRecorder.clear() - rule.onNodeWithText("This is a pinned message").performClick() + onNodeWithText("This is a pinned message").performClick() eventsRecorder.assertSingle(TimelineEvent.FocusOnEvent(AN_EVENT_ID, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds)) } @Test - fun `clicking on successor room button emits expected event`() { + fun `clicking on successor room button emits expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val successorRoomId = RoomId("!successor:server.org") val state = aMessagesState( @@ -606,18 +605,18 @@ class MessagesViewTest { ), timelineState = aTimelineState(eventSink = eventsRecorder) ) - rule.setMessagesView(state = state) + setMessagesView(state = state) // Clear initial 'LoadMore' event emitted when setting the state eventsRecorder.clear() - val text = rule.activity.getString(R.string.screen_room_timeline_tombstoned_room_action) + val text = activity!!.getString(R.string.screen_room_timeline_tombstoned_room_action) // The bottomsheet subcompose seems to make the node to appear twice - rule.onAllNodesWithText(text).onFirst().performClick() + onAllNodesWithText(text).onFirst().performClick() eventsRecorder.assertSingle(TimelineEvent.NavigateToPredecessorOrSuccessorRoom(successorRoomId)) } @Test - fun `clicking on threads list button calls the expected function`() { + fun `clicking on threads list button calls the expected function`() = runAndroidComposeUiTest { val state = aMessagesState( threads = MessagesState.Threads( hasThreads = true, @@ -625,28 +624,28 @@ class MessagesViewTest { ) ) val onThreadsListClicked = lambdaRecorder {} - rule.setMessagesView( + setMessagesView( state = state, onThreadsListClicked = onThreadsListClicked, ) - rule.onNodeWithContentDescription("Threads").performClick() + onNodeWithContentDescription("Threads").performClick() onThreadsListClicked.assertions().isCalledOnce() } @Test - fun `no banner shown when there is no successor room`() { + fun `no banner shown when there is no successor room`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( successorRoom = null, eventSink = eventsRecorder ) - rule.setMessagesView(state = state) - rule.assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_message) - rule.assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_action) + setMessagesView(state = state) + assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_message) + assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_action) } } -private fun AndroidComposeTestRule.setMessagesView( +private fun AndroidComposeUiTest.setMessagesView( state: MessagesState, onBackClick: () -> Unit = EnsureNeverCalled(), onRoomDetailsClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt index 24779ba78a..0ee342513a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.crypto.identity import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.core.UserId @@ -21,19 +24,15 @@ import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class IdentityChangeStateViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `show and resolve pin violation`() { + fun `show and resolve pin violation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIdentityChangeStateView( + setIdentityChangeStateView( state = anIdentityChangeState( listOf( RoomMemberIdentityStateChange( @@ -45,18 +44,18 @@ class IdentityChangeStateViewTest { ), ) - rule.onNodeWithText("identity was reset", substring = true).assertExists("should display pin violation warning") - rule.onNodeWithText("@alice:localhost", substring = true).assertExists("should display user mxid") - rule.onNodeWithText("Alice", substring = true).assertExists("should display user displayname") + onNodeWithText("identity was reset", substring = true).assertExists("should display pin violation warning") + onNodeWithText("@alice:localhost", substring = true).assertExists("should display user mxid") + onNodeWithText("Alice", substring = true).assertExists("should display user displayname") - rule.clickOn(res = CommonStrings.action_dismiss) + clickOn(res = CommonStrings.action_dismiss) eventsRecorder.assertSingle(IdentityChangeEvent.PinIdentity(UserId("@alice:localhost"))) } @Test - fun `show and resolve verification violation`() { + fun `show and resolve verification violation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIdentityChangeStateView( + setIdentityChangeStateView( state = anIdentityChangeState( listOf( RoomMemberIdentityStateChange( @@ -68,17 +67,17 @@ class IdentityChangeStateViewTest { ), ) - rule.onNodeWithText("identity was reset", substring = true).assertExists("should display verification violation warning") - rule.onNodeWithText("@alice:localhost", substring = true).assertExists("should display user mxid") - rule.onNodeWithText("Alice", substring = true).assertExists("should display user displayname") + onNodeWithText("identity was reset", substring = true).assertExists("should display verification violation warning") + onNodeWithText("@alice:localhost", substring = true).assertExists("should display user mxid") + onNodeWithText("Alice", substring = true).assertExists("should display user displayname") - rule.clickOn(res = CommonStrings.crypto_identity_change_withdraw_verification_action) + clickOn(res = CommonStrings.crypto_identity_change_withdraw_verification_action) eventsRecorder.assertSingle(IdentityChangeEvent.WithdrawVerification(UserId("@alice:localhost"))) } @Test - fun `Should not show any banner if no violations`() { - rule.setIdentityChangeStateView( + fun `Should not show any banner if no violations`() = runAndroidComposeUiTest { + setIdentityChangeStateView( state = anIdentityChangeState( listOf( RoomMemberIdentityStateChange( @@ -93,10 +92,10 @@ class IdentityChangeStateViewTest { ), ) - rule.onNodeWithText("identity was reset", substring = true).assertDoesNotExist() + onNodeWithText("identity was reset", substring = true).assertDoesNotExist() } - private fun AndroidComposeTestRule.setIdentityChangeStateView( + private fun AndroidComposeUiTest.setIdentityChangeStateView( state: IdentityChangeState, ) { setContent { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt index 02767fbeb9..07a0fd5f94 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/sendfailure/resolve/ResolveVerifiedUserSendFailureViewTest.kt @@ -6,54 +6,53 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.crypto.sendfailure.resolve import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ResolveVerifiedUserSendFailureViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on resolve and resend emit the expected event`() { + fun `clicking on resolve and resend emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResolveVerifiedUserSendFailureView( + setResolveVerifiedUserSendFailureView( state = aResolveVerifiedUserSendFailureState( verifiedUserSendFailure = aChangedIdentitySendFailure(), eventSink = eventsRecorder, ), ) - rule.clickOn(res = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title) + clickOn(res = CommonStrings.screen_resolve_send_failure_changed_identity_primary_button_title) eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvent.ResolveAndResend) } @Test - fun `clicking on retry emit the expected event`() { + fun `clicking on retry emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResolveVerifiedUserSendFailureView( + setResolveVerifiedUserSendFailureView( state = aResolveVerifiedUserSendFailureState( verifiedUserSendFailure = aChangedIdentitySendFailure(), eventSink = eventsRecorder, ), ) - rule.clickOn(res = CommonStrings.action_retry) + clickOn(res = CommonStrings.action_retry) eventsRecorder.assertSingle(ResolveVerifiedUserSendFailureEvent.Retry) } - private fun AndroidComposeTestRule.setResolveVerifiedUserSendFailureView( + private fun AndroidComposeUiTest.setResolveVerifiedUserSendFailureView( state: ResolveVerifiedUserSendFailureState, ) { setSafeContent { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/link/LinkViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/link/LinkViewTest.kt index e198ea9043..b656430466 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/link/LinkViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/link/LinkViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.link import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.ui.strings.CommonStrings @@ -19,51 +22,46 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.wysiwyg.link.Link -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LinkViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on cancel emits the expected event`() { + fun `clicking on cancel emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLinkView( + setLinkView( aLinkState( linkClick = ConfirmingLinkClick(aLink), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle( LinkEvent.Cancel ) } @Test - fun `clicking on continue emits the expected event`() { + fun `clicking on continue emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setLinkView( + setLinkView( aLinkState( linkClick = ConfirmingLinkClick(aLink), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle( LinkEvent.Confirm ) } @Test - fun `success state invokes the callback and emits the expected event`() { + fun `success state invokes the callback and emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnceWithParam(aLink) { callback -> - rule.setLinkView( + setLinkView( aLinkState( linkClick = AsyncAction.Success(aLink), eventSink = eventsRecorder, @@ -77,7 +75,7 @@ class LinkViewTest { } } -private fun AndroidComposeTestRule.setLinkView( +private fun AndroidComposeUiTest.setLinkView( state: LinkState, onLinkValid: (Link) -> Unit = EnsureNeverCalledWithParam(), ) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerViewTest.kt index 2c33e348c0..546731ff87 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.pinned.banner import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.ui.strings.CommonStrings @@ -22,49 +25,45 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PinnedMessagesBannerViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on the banner invoke expected callback`() { + fun `clicking on the banner invoke expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aLoadedPinnedMessagesBannerState( eventSink = eventsRecorder ) val pinnedEventId = state.currentPinnedMessage.eventId ensureCalledOnceWithParam(pinnedEventId) { callback -> - rule.setPinnedMessagesBannerView( + setPinnedMessagesBannerView( state = state, onClick = callback ) - rule.onRoot().performClick() + onRoot().performClick() eventsRecorder.assertSingle(PinnedMessagesBannerEvent.MoveToNextPinned) } } @Test - fun `clicking on view all emit the expected event`() { + fun `clicking on view all emit the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = true) val state = aLoadedPinnedMessagesBannerState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setPinnedMessagesBannerView( + setPinnedMessagesBannerView( state = state, onViewAllClick = callback ) - rule.clickOn(CommonStrings.screen_room_pinned_banner_view_all_button_title) + clickOn(CommonStrings.screen_room_pinned_banner_view_all_button_title) } } } -private fun AndroidComposeTestRule.setPinnedMessagesBannerView( +private fun AndroidComposeUiTest.setPinnedMessagesBannerView( state: PinnedMessagesBannerState, onClick: (EventId) -> Unit = EnsureNeverCalledWithParam(), onViewAllClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt index 41671b71c1..9c10abb631 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt @@ -6,16 +6,19 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.pinned.list import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.longClick import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.messages.impl.actionlist.ActionListEvent import io.element.android.features.messages.impl.actionlist.anActionListState @@ -31,33 +34,28 @@ import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.setSafeContent import io.element.android.wysiwyg.link.Link -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PinnedMessagesListViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back calls the expected callback`() { + fun `clicking on back calls the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aLoadedPinnedMessagesListState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setPinnedMessagesListView( + setPinnedMessagesListView( state = state, onBackClick = callback ) - rule.pressBack() + pressBack() } } @Test - fun `click on an event calls the expected callback`() { + fun `click on an event calls the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val content = aTimelineItemFileContent() val state = aLoadedPinnedMessagesListState( @@ -67,16 +65,16 @@ class PinnedMessagesListViewTest { val event = state.timelineItems.first() as TimelineItem.Event ensureCalledOnceWithParam(event) { callback -> - rule.setPinnedMessagesListView( + setPinnedMessagesListView( state = state, onEventClick = callback ) - rule.onAllNodesWithText(content.filename).onFirst().performClick() + onAllNodesWithText(content.filename).onFirst().performClick() } } @Test - fun `long click on an event emits the expected event`() { + fun `long click on an event emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = true) val content = aTimelineItemFileContent() val state = aLoadedPinnedMessagesListState( @@ -84,10 +82,10 @@ class PinnedMessagesListViewTest { actionListState = anActionListState(eventSink = eventsRecorder) ) - rule.setPinnedMessagesListView( + setPinnedMessagesListView( state = state, ) - rule.onAllNodesWithText(content.filename).onFirst() + onAllNodesWithText(content.filename).onFirst() .performTouchInput { longClick() } @@ -96,7 +94,7 @@ class PinnedMessagesListViewTest { } } -private fun AndroidComposeTestRule.setPinnedMessagesListView( +private fun AndroidComposeUiTest.setPinnedMessagesListView( state: PinnedMessagesListState, onBackClick: () -> Unit = EnsureNeverCalled(), onEventClick: (event: TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt index 315d9c459c..9e98f0fa49 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/DefaultHtmlConverterProviderTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.timeline import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runComposeUiTest import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -18,15 +21,12 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider import io.element.android.libraries.textcomposer.mentions.MentionSpanTheme -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class DefaultHtmlConverterProviderTest { - @get:Rule val composeTestRule = createComposeRule() - private val provider = DefaultHtmlConverterProvider( mentionSpanProvider = MentionSpanProvider( permalinkParser = FakePermalinkParser(), @@ -43,8 +43,8 @@ class DefaultHtmlConverterProviderTest { } @Test - fun `calling provide after calling Update first should return an HtmlConverter`() { - composeTestRule.setContent { + fun `calling provide after calling Update first should return an HtmlConverter`() = runComposeUiTest { + setContent { CompositionLocalProvider(LocalInspectionMode provides true) { provider.Update() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt index 3a0b0e1224..2138d4ced2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt @@ -6,15 +6,18 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.timeline import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToIndex +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.messages.impl.timeline.components.MessageShieldData import io.element.android.features.messages.impl.timeline.components.aCriticalShield @@ -39,19 +42,15 @@ import io.element.android.wysiwyg.link.Link import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import org.junit.Ignore -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class TimelineViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `reaching the end of the timeline with more events to load emits a LoadMore event`() { + fun `reaching the end of the timeline with more events to load emits a LoadMore event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf( TimelineItem.Virtual( @@ -66,9 +65,9 @@ class TimelineViewTest { } @Test - fun `reaching the end of the timeline does not send a LoadMore event`() { + fun `reaching the end of the timeline does not send a LoadMore event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())), eventSink = eventsRecorder, @@ -78,9 +77,9 @@ class TimelineViewTest { } @Test - fun `scroll to bottom on live timeline does not emit the Event`() { + fun `scroll to bottom on live timeline does not emit the Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())), isLive = true, @@ -92,14 +91,14 @@ class TimelineViewTest { eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0)) eventsRecorder.clear() - val contentDescription = rule.activity.getString(CommonStrings.a11y_jump_to_bottom) - rule.onNodeWithContentDescription(contentDescription).performClick() + val contentDescription = activity!!.getString(CommonStrings.a11y_jump_to_bottom) + onNodeWithContentDescription(contentDescription).performClick() } @Test - fun `scroll to bottom on detached timeline emits the expected Event`() { + fun `scroll to bottom on detached timeline emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())), isLive = false, @@ -110,15 +109,15 @@ class TimelineViewTest { eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0)) eventsRecorder.clear() - val contentDescription = rule.activity.getString(CommonStrings.a11y_jump_to_bottom) - rule.onNodeWithContentDescription(contentDescription).performClick() + val contentDescription = activity!!.getString(CommonStrings.a11y_jump_to_bottom) + onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertSingle(TimelineEvent.JumpToLive) } @Test - fun `an empty timeline triggers a prefetch`() { + fun `an empty timeline triggers a prefetch`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf(), eventSink = eventsRecorder, @@ -129,9 +128,9 @@ class TimelineViewTest { } @Test - fun `show shield dialog`() { + fun `show shield dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf( aTimelineItemEvent( @@ -143,8 +142,8 @@ class TimelineViewTest { eventSink = eventsRecorder, ), ) - val contentDescription = rule.activity.getString(CommonStrings.a11y_encryption_details) - rule.onNodeWithContentDescription(contentDescription).performClick() + val contentDescription = activity!!.getString(CommonStrings.a11y_encryption_details) + onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertList( listOf( TimelineEvent.OnScrollFinished(0), @@ -154,9 +153,9 @@ class TimelineViewTest { } @Test - fun `hide shield dialog`() { + fun `hide shield dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = persistentListOf(aTimelineItemEvent(content = aTimelineItemImageContent())), isLive = false, @@ -167,16 +166,16 @@ class TimelineViewTest { eventsRecorder.assertSingle(TimelineEvent.OnScrollFinished(firstIndex = 0)) eventsRecorder.clear() - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(TimelineEvent.HideShieldDialog) } @Ignore( "performScrollToIndex in compose tests no longer sets LazyListState.isScrollInProgress to true, so the LoadMore event is not emitted." + - "This needs to be reworked to use a different approach to check the LoadMore event was emitted." + "This needs to be reworked to use a different approach to check the LoadMore event was emitted." ) @Test - fun `scrolling near to the start of the loaded items triggers a pre-fetch`() { + fun `scrolling near to the start of the loaded items triggers a pre-fetch`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val items = List(200) { aTimelineItemEvent( @@ -185,7 +184,7 @@ class TimelineViewTest { ) }.toImmutableList() - rule.setTimelineView( + setTimelineView( state = aTimelineState( timelineItems = items, eventSink = eventsRecorder, @@ -194,9 +193,9 @@ class TimelineViewTest { ), ) - rule.onNodeWithTag("timeline").performScrollToIndex(180) + onNodeWithTag("timeline").performScrollToIndex(180) - rule.mainClock.advanceTimeBy(1000) + mainClock.advanceTimeBy(1000) eventsRecorder.assertList( listOf( @@ -207,7 +206,7 @@ class TimelineViewTest { } } -private fun AndroidComposeTestRule.setTimelineView( +private fun AndroidComposeUiTest.setTimelineView( state: TimelineState, timelineProtectionState: TimelineProtectionState = aTimelineProtectionState(), onUserDataClick: (MatrixUser) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt index 64b5216d2e..40671e4bf8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollViewTest.kt @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.timeline.components.event import androidx.activity.ComponentActivity +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent @@ -20,14 +23,11 @@ import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressTag -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class TimelineItemPollViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test fun `answering a poll with first answer should emit a PollAnswerSelected event`() { testAnswer(answerIndex = 0) @@ -38,17 +38,17 @@ class TimelineItemPollViewTest { testAnswer(answerIndex = 1) } - private fun testAnswer(answerIndex: Int) { + private fun testAnswer(answerIndex: Int) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val content = aTimelineItemPollContent() - rule.setContent { + setContent { TimelineItemPollView( content = content, eventSink = eventsRecorder ) } val answer = content.answerItems[answerIndex].answer - rule.onNode( + onNode( matcher = hasText(answer.text), useUnmergedTree = true, ).performClick() @@ -56,38 +56,38 @@ class TimelineItemPollViewTest { } @Test - fun `editing a poll should emit a PollEditClicked event`() { + fun `editing a poll should emit a PollEditClicked event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val content = aTimelineItemPollContent( isMine = true, isEditable = true, ) - rule.setContent { + setContent { TimelineItemPollView( content = content, eventSink = eventsRecorder ) } - rule.clickOn(CommonStrings.action_edit_poll) + clickOn(CommonStrings.action_edit_poll) eventsRecorder.assertSingle(TimelineEvent.EditPoll(content.eventId!!)) } @Test - fun `closing a poll should emit a PollEndClicked event`() { + fun `closing a poll should emit a PollEndClicked event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val content = aTimelineItemPollContent( isMine = true, ) - rule.setContent { + setContent { TimelineItemPollView( content = content, eventSink = eventsRecorder ) } - rule.clickOn(CommonStrings.action_end_poll) + clickOn(CommonStrings.action_end_poll) // A confirmation dialog should be shown eventsRecorder.assertEmpty() - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(TimelineEvent.EndPoll(content.eventId!!)) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineTextViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineTextViewTest.kt index 154225aa7a..7b8597f05a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineTextViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineTextViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.timeline.components.event import android.text.SpannableString import android.text.SpannedString import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -38,45 +41,40 @@ import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.wysiwyg.view.spans.CustomMentionSpan import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.test.runTest -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class TimelineTextViewTest { - @get:Rule val rule = createAndroidComposeRule() - private val mentionSpanTheme = MentionSpanTheme(currentUserId = A_USER_ID) private val formatLambda = lambdaRecorder { mentionType -> mentionType.toString() } private val mentionSpanFormatter = FakeMentionSpanFormatter(formatLambda) @Test - fun `getTextWithResolvedMentions - does nothing for a non spannable CharSequence`() = runTest { + fun `getTextWithResolvedMentions - does nothing for a non spannable CharSequence`() = runAndroidComposeUiTest { val charSequence = "Hello @alice:example.com" val mentionSpanUpdater = aMentionSpanUpdater() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) assertThat(result.getMentionSpans()).isEmpty() assert(formatLambda).isNeverCalled() } @Test - fun `getTextWithResolvedMentions - does nothing if there are no mentions`() = runTest { + fun `getTextWithResolvedMentions - does nothing if there are no mentions`() = runAndroidComposeUiTest { val charSequence = SpannableString("Hello @alice:example.com") val mentionSpanUpdater = aMentionSpanUpdater() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) assertThat(result.getMentionSpans()).isEmpty() assert(formatLambda).isNeverCalled() } @Test - fun `getTextWithResolvedMentions - just returns the body if there is no formattedBody`() = runTest { + fun `getTextWithResolvedMentions - just returns the body if there is no formattedBody`() = runAndroidComposeUiTest { val charSequence = "Hello @alice:example.com" val mentionSpanUpdater = aMentionSpanUpdater() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(body = charSequence, formattedBody = null)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(body = charSequence, formattedBody = null)) assertThat(result.getMentionSpans()).isEmpty() assertThat(result.toString()).isEqualTo(charSequence) @@ -84,7 +82,7 @@ class TimelineTextViewTest { } @Test - fun `getTextWithResolvedMentions - with Room mention format correctly`() = runTest { + fun `getTextWithResolvedMentions - with Room mention format correctly`() = runAndroidComposeUiTest { val mentionType = MentionType.Room(roomIdOrAlias = A_ROOM_ID_2.toRoomIdOrAlias()) val charSequence = buildSpannedString { append("Hello ") @@ -93,7 +91,7 @@ class TimelineTextViewTest { } } val mentionSpanUpdater = aMentionSpanUpdater() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) val expectedDisplayText = mentionType.toString() assertThat(result.getMentionSpans().firstOrNull()?.displayText.toString()).isEqualTo(expectedDisplayText) @@ -102,7 +100,7 @@ class TimelineTextViewTest { } @Test - fun `getTextWithResolvedMentions - replaces MentionSpan's text`() = runTest { + fun `getTextWithResolvedMentions - replaces MentionSpan's text`() = runAndroidComposeUiTest { val mentionType = MentionType.User(userId = A_USER_ID) val charSequence = buildSpannedString { append("Hello ") @@ -111,7 +109,7 @@ class TimelineTextViewTest { } } val mentionSpanUpdater = aMentionSpanUpdater() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) val expectedDisplayText = mentionType.toString() assertThat(result.getMentionSpans().firstOrNull()?.displayText.toString()).isEqualTo(expectedDisplayText) @@ -119,7 +117,7 @@ class TimelineTextViewTest { } @Test - fun `getTextWithResolvedMentions - replaces MentionSpan's text inside CustomMentionSpan`() = runTest { + fun `getTextWithResolvedMentions - replaces MentionSpan's text inside CustomMentionSpan`() = runAndroidComposeUiTest { val mentionType = MentionType.User(userId = A_USER_ID) val charSequence = buildSpannedString { append("Hello ") @@ -129,12 +127,12 @@ class TimelineTextViewTest { } val mentionSpanUpdater = aMentionSpanUpdater() val expectedDisplayText = mentionType.toString() - val result = rule.getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) + val result = getText(mentionSpanUpdater, aTextContentWithFormattedBody(charSequence)) assertThat(result.getMentionSpans().firstOrNull()?.displayText.toString()).isEqualTo(expectedDisplayText) assert(formatLambda).isCalledOnce() } - private suspend fun AndroidComposeTestRule.getText( + private suspend fun AndroidComposeUiTest.getText( mentionSpanUpdater: MentionSpanUpdater, content: TimelineItemTextBasedContent, ): CharSequence { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedViewTest.kt index af3acee6a2..8050278fb2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/ProtectedViewTest.kt @@ -6,56 +6,55 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.messages.impl.timeline.protection import androidx.activity.ComponentActivity import androidx.compose.runtime.Composable -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.lambda.lambdaError -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ProtectedViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `when hideContent is false, the content is rendered`() { - rule.setProtectedView( + fun `when hideContent is false, the content is rendered`() = runAndroidComposeUiTest { + setProtectedView( hideContent = false, content = { Text("Hello") } ) - rule.onNodeWithText("Hello").assertExists() + onNodeWithText("Hello").assertExists() } @Test - fun `when hideContent is true, the content is not rendered, and user can reveal it`() { + fun `when hideContent is true, the content is not rendered, and user can reveal it`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setProtectedView( + setProtectedView( hideContent = true, onShowClick = it, content = { Text("Hello") } ) - rule.onNodeWithText("Hello").assertDoesNotExist() - rule.clickOn(CommonStrings.action_show) + onNodeWithText("Hello").assertDoesNotExist() + clickOn(CommonStrings.action_show) } } } -private fun AndroidComposeTestRule.setProtectedView( +private fun AndroidComposeUiTest.setProtectedView( hideContent: Boolean = false, onShowClick: () -> Unit = { lambdaError() }, content: @Composable () -> Unit = {}, diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt index 1ff25a0a81..a6b97c554c 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/PollHistoryViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.poll.impl.history import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.poll.api.pollcontent.aPollContentState import io.element.android.features.poll.impl.R @@ -26,34 +29,29 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class PollHistoryViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setPollHistoryViewView( + setPollHistoryViewView( aPollHistoryState( eventSink = eventsRecorder ), goBack = it ) - rule.pressBack() + pressBack() } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on edit poll invokes the expected callback`() { + fun `clicking on edit poll invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val eventId = EventId("\$anEventId") val state = aPollHistoryState( @@ -69,17 +67,17 @@ class PollHistoryViewTest { eventSink = eventsRecorder ) ensureCalledOnceWithParam(eventId) { - rule.setPollHistoryViewView( + setPollHistoryViewView( state = state, onEditPoll = it ) - rule.clickOn(CommonStrings.action_edit_poll) + clickOn(CommonStrings.action_edit_poll) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on poll end emits the expected Event`() { + fun `clicking on poll end emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val eventId = EventId("\$anEventId") val state = aPollHistoryState( @@ -95,16 +93,16 @@ class PollHistoryViewTest { ), eventSink = eventsRecorder ) - rule.setPollHistoryViewView( + setPollHistoryViewView( state = state, ) - rule.clickOn(CommonStrings.action_end_poll) + clickOn(CommonStrings.action_end_poll) // Cancel the dialog - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) // Do it again, and confirm the dialog - rule.clickOn(CommonStrings.action_end_poll) + clickOn(CommonStrings.action_end_poll) eventsRecorder.assertEmpty() - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle( PollHistoryEvents.EndPoll(eventId) ) @@ -112,7 +110,7 @@ class PollHistoryViewTest { @Config(qualifiers = "h1024dp") @Test - fun `clicking on poll answer emits the expected Event`() { + fun `clicking on poll answer emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val eventId = EventId("\$anEventId") val state = aPollHistoryState( @@ -129,10 +127,10 @@ class PollHistoryViewTest { eventSink = eventsRecorder ) val answer = state.pollHistoryItems.ongoing.first().state.answerItems.first().answer - rule.setPollHistoryViewView( + setPollHistoryViewView( state = state, ) - rule.onNodeWithText( + onNodeWithText( text = answer.text, useUnmergedTree = true, ).performClick() @@ -142,14 +140,14 @@ class PollHistoryViewTest { } @Test - fun `clicking on past tab emits the expected Event`() { + fun `clicking on past tab emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPollHistoryViewView( + setPollHistoryViewView( aPollHistoryState( eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_polls_history_filter_past) + clickOn(R.string.screen_polls_history_filter_past) eventsRecorder.assertSingle( PollHistoryEvents.SelectFilter(filter = PollHistoryFilter.PAST) ) @@ -157,22 +155,22 @@ class PollHistoryViewTest { @Config(qualifiers = "h1024dp") @Test - fun `clicking on load more emits the expected Event`() { + fun `clicking on load more emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPollHistoryViewView( + setPollHistoryViewView( aPollHistoryState( hasMoreToLoad = true, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_load_more) + clickOn(CommonStrings.action_load_more) eventsRecorder.assertSingle( PollHistoryEvents.LoadMore ) } } -private fun AndroidComposeTestRule.setPollHistoryViewView( +private fun AndroidComposeUiTest.setPollHistoryViewView( state: PollHistoryState, onEditPoll: (EventId) -> Unit = EnsureNeverCalledWithParam(), goBack: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutViewTest.kt index 258e9855de..e7ce526843 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.about import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -19,51 +22,47 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class AboutViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes back callback`() { + fun `clicking on back invokes back callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setAboutView( + setAboutView( anAboutState(), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on an item invokes the expected callback`() { + fun `clicking on an item invokes the expected callback`() = runAndroidComposeUiTest { val state = anAboutState() ensureCalledOnceWithParam(state.elementLegals.first()) { callback -> - rule.setAboutView( + setAboutView( state, onElementLegalClick = callback, ) - rule.clickOn(state.elementLegals.first().titleRes) + clickOn(state.elementLegals.first().titleRes) } } @Test - fun `clicking on the open source licenses invokes the expected callback`() { + fun `clicking on the open source licenses invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setAboutView( + setAboutView( anAboutState(), onOpenSourceLicensesClick = callback, ) - rule.clickOn(CommonStrings.common_open_source_licenses) + clickOn(CommonStrings.common_open_source_licenses) } } } -private fun AndroidComposeTestRule.setAboutView( +private fun AndroidComposeUiTest.setAboutView( state: AboutState, onElementLegalClick: (ElementLegal) -> Unit = EnsureNeverCalledWithParam(), onOpenSourceLicensesClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt index e46e350415..b6fe5c3d0b 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.advanced import androidx.activity.ComponentActivity import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Interaction @@ -30,104 +33,99 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import kotlinx.collections.immutable.toImmutableList -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class AdvancedSettingsViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on other theme emits the expected event`() { + fun `clicking on other theme emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.common_appearance) - rule.clickOn(CommonStrings.common_dark) + clickOn(CommonStrings.common_appearance) + clickOn(CommonStrings.common_dark) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark)) } @Test - fun `black theme is shown when available`() { - rule.setAdvancedSettingsView( + fun `black theme is shown when available`() = runAndroidComposeUiTest { + setAdvancedSettingsView( state = aAdvancedSettingsState( availableThemeOptions = ThemeOption.entries.toImmutableList(), ), ) - rule.clickOn(CommonStrings.common_appearance) - rule.run { - val text = activity.getString(CommonStrings.common_black) + clickOn(CommonStrings.common_appearance) + run { + val text = activity!!.getString(CommonStrings.common_black) onNodeWithText(text).assertExists() } } @Test - fun `black theme is hidden when unavailable`() { - rule.setAdvancedSettingsView( + fun `black theme is hidden when unavailable`() = runAndroidComposeUiTest { + setAdvancedSettingsView( state = aAdvancedSettingsState( availableThemeOptions = ThemeOption.entries.filterNot { it == ThemeOption.Black }.toImmutableList(), ), ) - rule.clickOn(CommonStrings.common_appearance) - rule.assertNoNodeWithText(CommonStrings.common_black) + clickOn(CommonStrings.common_appearance) + assertNoNodeWithText(CommonStrings.common_black) } @Test - fun `clicking on View source emits the expected event`() { + fun `clicking on View source emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_view_source) + clickOn(CommonStrings.action_view_source) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetDeveloperModeEnabled(true)) } @Test - fun `clicking on Share presence emits the expected event`() { + fun `clicking on Share presence emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_advanced_settings_share_presence) + clickOn(R.string.screen_advanced_settings_share_presence) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetSharePresenceEnabled(true)) } @Test - fun `clicking on media to enable compression emits the expected event`() { + fun `clicking on media to enable compression emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val analyticsService = FakeAnalyticsService() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, ), analyticsService = analyticsService ) - rule.clickOn(R.string.screen_advanced_settings_media_compression_description) + clickOn(R.string.screen_advanced_settings_media_compression_description) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(true)) assertThat(analyticsService.capturedEvents).isEqualTo( listOf( @@ -139,17 +137,17 @@ class AdvancedSettingsViewTest { } @Test - fun `clicking on media to disable compression emits the expected event`() { + fun `clicking on media to disable compression emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val analyticsService = FakeAnalyticsService() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( mediaOptimizationState = MediaOptimizationState.AllMedia(isEnabled = true), eventSink = eventsRecorder, ), analyticsService = analyticsService ) - rule.clickOn(R.string.screen_advanced_settings_media_compression_description) + clickOn(R.string.screen_advanced_settings_media_compression_description) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetCompressMedia(false)) assertThat(analyticsService.capturedEvents).isEqualTo( listOf( @@ -162,65 +160,65 @@ class AdvancedSettingsViewTest { @Test @Config(qualifiers = "h1080dp") - fun `clicking on hide invite avatars emits the expected event`() { + fun `clicking on hide invite avatars emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, hideInviteAvatars = false ), ) - rule.clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title) + clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetHideInviteAvatars(true)) } @Test @Config(qualifiers = "h1080dp") - fun `clicking on timeline media preview always hide emits the expected event`() { + fun `clicking on timeline media preview always hide emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, timelineMediaPreviewValue = MediaPreviewValue.On ), ) - rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide) + clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off)) } @Test @Config(qualifiers = "h1080dp") - fun `clicking on timeline media preview private rooms emits the expected event`() { + fun `clicking on timeline media preview private rooms emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, timelineMediaPreviewValue = MediaPreviewValue.On ), ) - rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms) + clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Private)) } @Test @Config(qualifiers = "h1080dp") - fun `clicking on timeline media preview always show emits the expected event`() { + fun `clicking on timeline media preview always show emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, timelineMediaPreviewValue = MediaPreviewValue.Off ), ) - rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_show) + clickOn(R.string.screen_advanced_settings_show_media_timeline_always_show) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.On)) } @Test @Config(qualifiers = "h1080dp") - fun `hide invite avatars toggle is disabled when action is loading`() { + fun `hide invite avatars toggle is disabled when action is loading`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, hideInviteAvatars = false, @@ -228,14 +226,14 @@ class AdvancedSettingsViewTest { ), ) // The toggle should be disabled, so clicking should not emit any events - rule.clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title) + clickOn(R.string.screen_advanced_settings_hide_invite_avatars_toggle_title) } @Test @Config(qualifiers = "h1080dp") - fun `timeline media preview options are disabled when action is loading`() { + fun `timeline media preview options are disabled when action is loading`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setAdvancedSettingsView( + setAdvancedSettingsView( state = aAdvancedSettingsState( eventSink = eventsRecorder, timelineMediaPreviewValue = MediaPreviewValue.On, @@ -243,12 +241,12 @@ class AdvancedSettingsViewTest { ), ) // The options should be disabled, so clicking should not emit any events - rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide) - rule.clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms) + clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide) + clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms) } } -private fun AndroidComposeTestRule.setAdvancedSettingsView( +private fun AndroidComposeUiTest.setAdvancedSettingsView( state: AdvancedSettingsState, analyticsService: AnalyticsService = FakeAnalyticsService(), onBackClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt index b3549762ab..993d14caab 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUserViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.blockedusers import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.preferences.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -23,72 +26,67 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class BlockedUserViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes back callback`() { + fun `clicking on back invokes back callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setBlockedUsersView( + setBlockedUsersView( aBlockedUsersState( eventSink = eventsRecorder ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on a user emits the expected Event`() { + fun `clicking on a user emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val userList = aMatrixUserList() - rule.setBlockedUsersView( + setBlockedUsersView( aBlockedUsersState( blockedUsers = userList, eventSink = eventsRecorder ), ) - rule.onNodeWithText(userList.first().displayName.orEmpty()).performClick() + onNodeWithText(userList.first().displayName.orEmpty()).performClick() eventsRecorder.assertSingle(BlockedUsersEvents.Unblock(userList.first().userId)) } @Test - fun `clicking on cancel sends a BlockedUsersEvents`() { + fun `clicking on cancel sends a BlockedUsersEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setBlockedUsersView( + setBlockedUsersView( aBlockedUsersState( unblockUserAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(BlockedUsersEvents.Cancel) } @Test - fun `clicking on confirm sends a BlockedUsersEvents`() { + fun `clicking on confirm sends a BlockedUsersEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setBlockedUsersView( + setBlockedUsersView( aBlockedUsersState( unblockUserAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_blocked_users_unblock_alert_action) + clickOn(R.string.screen_blocked_users_unblock_alert_action) eventsRecorder.assertSingle(BlockedUsersEvents.ConfirmUnblock) } } -private fun AndroidComposeTestRule.setBlockedUsersView( +private fun AndroidComposeUiTest.setBlockedUsersView( state: BlockedUsersState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt index d4d02d7de9..61d7278a8a 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.developer import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.preferences.impl.R import io.element.android.tests.testutils.EnsureNeverCalled @@ -20,76 +23,71 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class DeveloperSettingsViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setDeveloperSettingsView( + setDeveloperSettingsView( state = aDeveloperSettingsState( eventSink = eventsRecorder ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Config(qualifiers = "h2000dp") @Test - fun `clicking on push history notification invokes the expected callback`() { + fun `clicking on push history notification invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setDeveloperSettingsView( + setDeveloperSettingsView( state = aDeveloperSettingsState( eventSink = eventsRecorder ), onPushHistoryClick = it ) - rule.clickOn(R.string.troubleshoot_notifications_entry_point_push_history_title) + clickOn(R.string.troubleshoot_notifications_entry_point_push_history_title) } } @Config(qualifiers = "h2000dp") @Test - fun `clicking on open showkase invokes the expected callback`() { + fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setDeveloperSettingsView( + setDeveloperSettingsView( state = aDeveloperSettingsState( eventSink = eventsRecorder ), onOpenShowkase = it ) - rule.onNodeWithText("Open Showkase browser").performClick() + onNodeWithText("Open Showkase browser").performClick() } } @Config(qualifiers = "h2200dp") @Test - fun `clicking on clear cache emits the expected event`() { + fun `clicking on clear cache emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setDeveloperSettingsView( + setDeveloperSettingsView( state = aDeveloperSettingsState( eventSink = eventsRecorder ), ) - rule.onNodeWithText("Clear cache").performClick() + onNodeWithText("Clear cache").performClick() eventsRecorder.assertSingle(DeveloperSettingsEvents.ClearCache) } } -private fun AndroidComposeTestRule.setDeveloperSettingsView( +private fun AndroidComposeUiTest.setDeveloperSettingsView( state: DeveloperSettingsState, onOpenShowkase: () -> Unit = EnsureNeverCalled(), onPushHistoryClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPageTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPageTest.kt index 123f31ae8e..17218c6ab5 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPageTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPageTest.kt @@ -5,19 +5,22 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.developer.appsettings import androidx.activity.ComponentActivity +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.filterToOne import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.isDialog import androidx.compose.ui.test.isEditable import androidx.compose.ui.test.isFocusable -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem @@ -27,78 +30,73 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class AppDeveloperSettingsPageTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setAppDeveloperSettingsView( + setAppDeveloperSettingsView( state = anAppDeveloperSettingsState( eventSink = eventsRecorder ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Config(qualifiers = "h1500dp") @Test - fun `clicking on element call url open the dialogs and submit emits the expected event`() { + fun `clicking on element call url open the dialogs and submit emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAppDeveloperSettingsView( + setAppDeveloperSettingsView( state = anAppDeveloperSettingsState( eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_advanced_settings_element_call_base_url) - val textInputNode = rule.onAllNodes(isEditable().and(isFocusable())).filterToOne(hasAnyAncestor(isDialog())) + clickOn(R.string.screen_advanced_settings_element_call_base_url) + val textInputNode = onAllNodes(isEditable().and(isFocusable())).filterToOne(hasAnyAncestor(isDialog())) textInputNode.performTextInput("https://call.element.dev") - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(AppDeveloperSettingsEvent.SetCustomElementCallBaseUrl("https://call.element.dev")) } @Config(qualifiers = "h2000dp") @Test - fun `clicking on open showkase invokes the expected callback`() { + fun `clicking on open showkase invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setAppDeveloperSettingsView( + setAppDeveloperSettingsView( state = anAppDeveloperSettingsState( eventSink = eventsRecorder ), onOpenShowkase = it ) - rule.onNodeWithText("Open Showkase browser").performClick() + onNodeWithText("Open Showkase browser").performClick() } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on log level emits the expected event`() { + fun `clicking on log level emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAppDeveloperSettingsView( + setAppDeveloperSettingsView( state = anAppDeveloperSettingsState( eventSink = eventsRecorder ), ) - rule.onNodeWithText("Tracing log level").performClick() - rule.onNodeWithText("Debug").performClick() + onNodeWithText("Tracing log level").performClick() + onNodeWithText("Debug").performClick() eventsRecorder.assertSingle(AppDeveloperSettingsEvent.SetTracingLogLevel(LogLevelItem.DEBUG)) } } -private fun AndroidComposeTestRule.setAppDeveloperSettingsView( +private fun AndroidComposeUiTest.setAppDeveloperSettingsView( state: AppDeveloperSettingsState, onOpenShowkase: () -> Unit = EnsureNeverCalled(), onBackClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt index ea140abbd7..66ed0339a3 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.notifications import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.preferences.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -25,76 +28,71 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class NotificationSettingsViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnce { - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder ), onBackClick = it ) - rule.pressBack() + pressBack() } eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on troubleshoot notification invokes the expected callback`() { + fun `clicking on troubleshoot notification invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnce { - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder ), onTroubleshootNotificationsClick = it ) - rule.clickOn(R.string.troubleshoot_notifications_entry_point_title) + clickOn(R.string.troubleshoot_notifications_entry_point_title) } eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on group chats invokes the expected callback`() { + fun `clicking on group chats invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnceWithParam(false) { - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder ), onOpenEditDefault = it ) - rule.clickOn(R.string.screen_notification_settings_group_chats) + clickOn(R.string.screen_notification_settings_group_chats) } eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on direct chats invokes the expected callback`() { + fun `clicking on direct chats invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnceWithParam(true) { - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder ), onOpenEditDefault = it ) - rule.clickOn(R.string.screen_notification_settings_direct_chats) + clickOn(R.string.screen_notification_settings_direct_chats) } eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) } @@ -111,15 +109,15 @@ class NotificationSettingsViewTest { testNotificationToggle(false) } - private fun testNotificationToggle(initialState: Boolean) { + private fun testNotificationToggle(initialState: Boolean) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( appNotificationEnabled = initialState, eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_notification_settings_enable_notifications) + clickOn(R.string.screen_notification_settings_enable_notifications) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -140,15 +138,15 @@ class NotificationSettingsViewTest { testAtRoomToggle(false) } - private fun testAtRoomToggle(initialState: Boolean) { + private fun testAtRoomToggle(initialState: Boolean) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( atRoomNotificationsEnabled = initialState, eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_notification_settings_room_mention_label) + clickOn(R.string.screen_notification_settings_room_mention_label) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -169,15 +167,15 @@ class NotificationSettingsViewTest { testInvitationToggle(false) } - private fun testInvitationToggle(initialState: Boolean) { + private fun testInvitationToggle(initialState: Boolean) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( inviteForMeNotificationsEnabled = initialState, eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_notification_settings_invite_for_me_label) + clickOn(R.string.screen_notification_settings_invite_for_me_label) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -188,15 +186,15 @@ class NotificationSettingsViewTest { @Config(qualifiers = "h1024dp") @Test - fun `with an error configuration, clicking on continue emits the expected events`() { + fun `with an error configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( changeNotificationSettingAction = AsyncAction.Failure(AN_EXCEPTION), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -207,15 +205,15 @@ class NotificationSettingsViewTest { @Config(qualifiers = "h1024dp") @Test - fun `with invalid configuration, clicking on continue emits the expected events`() { + fun `with invalid configuration, clicking on continue emits the expected events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aInvalidNotificationSettingsState( fixFailed = false, eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -226,15 +224,15 @@ class NotificationSettingsViewTest { @Config(qualifiers = "h1024dp") @Test - fun `with invalid configuration and error, clicking on OK emits the expected events`() { + fun `with invalid configuration and error, clicking on OK emits the expected events`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aInvalidNotificationSettingsState( fixFailed = true, eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -245,14 +243,14 @@ class NotificationSettingsViewTest { @Config(qualifiers = "h1024dp") @Test - fun `clicking on Push notification provider emits the expected event`() { + fun `clicking on Push notification provider emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_advanced_settings_push_provider_android) + clickOn(R.string.screen_advanced_settings_push_provider_android) eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -262,16 +260,16 @@ class NotificationSettingsViewTest { } @Test - fun `clicking on a push provider emits the expected event`() { + fun `clicking on a push provider emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setNotificationSettingsView( + setNotificationSettingsView( state = aValidNotificationSettingsState( eventSink = eventsRecorder, showChangePushProviderDialog = true, availablePushDistributors = listOf(aDistributor("P1"), aDistributor("P2")) ), ) - rule.onNodeWithText("P2").performClick() + onNodeWithText("P2").performClick() eventsRecorder.assertList( listOf( NotificationSettingsEvents.RefreshSystemNotificationsEnabled, @@ -281,7 +279,7 @@ class NotificationSettingsViewTest { } } -private fun AndroidComposeTestRule.setNotificationSettingsView( +private fun AndroidComposeUiTest.setNotificationSettingsView( state: NotificationSettingsState, onOpenEditDefault: (isOneToOne: Boolean) -> Unit = EnsureNeverCalledWithParam(), onTroubleshootNotificationsClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootViewTest.kt index da91bdbf86..88ebbf64a1 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootViewTest.kt @@ -5,13 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.preferences.impl.R import io.element.android.libraries.matrix.api.user.MatrixUser @@ -25,49 +28,45 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PreferencesRootViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes back callback`() { + fun `clicking on back invokes back callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder ), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `click on User profile invokes the expected callback`() { + fun `click on User profile invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val user = aMatrixUser() ensureCalledOnceWithParam(user) { callback -> - rule.setView( + setView( aPreferencesRootState( myUser = user, eventSink = eventsRecorder, ), onOpenUserProfile = callback, ) - rule.onNodeWithText("Alice").performClick() + onNodeWithText("Alice").performClick() } } @Test - fun `clicking on other session sends a SwitchToSession`() { + fun `clicking on other session sends a SwitchToSession`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setView( + setView( aPreferencesRootState( isMultiAccountEnabled = true, otherSessions = listOf( @@ -79,366 +78,366 @@ class PreferencesRootViewTest { eventSink = eventsRecorder, ), ) - rule.onNodeWithText("Bob").performClick() + onNodeWithText("Bob").performClick() eventsRecorder.assertSingle(PreferencesRootEvent.SwitchToSession(A_USER_ID_2)) } @Test - fun `click on Add account invokes the expected callback`() { + fun `click on Add account invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( isMultiAccountEnabled = true, eventSink = eventsRecorder, ), onAddAccountClick = callback, ) - rule.clickOn(CommonStrings.common_add_another_account) + clickOn(CommonStrings.common_add_another_account) } } @Test - fun `when multi account is not enabled, item is not shown`() { + fun `when multi account is not enabled, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( isMultiAccountEnabled = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_add_another_account)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_add_another_account)).assertDoesNotExist() } @Test - fun `click on Encryption invokes the expected callback`() { + fun `click on Encryption invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( showSecureBackup = true, eventSink = eventsRecorder, ), onSecureBackupClick = callback, ) - rule.clickOn(CommonStrings.common_encryption) + clickOn(CommonStrings.common_encryption) } } @Test - fun `when showSecureBackup is false, item is not shown`() { + fun `when showSecureBackup is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( showSecureBackup = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_encryption)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_encryption)).assertDoesNotExist() } @Test - fun `click on Manage account invokes the expected callback`() { + fun `click on Manage account invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam("aUrl") { callback -> - rule.setView( + setView( aPreferencesRootState( accountManagementUrl = "aUrl", eventSink = eventsRecorder, ), onManageAccountClick = callback, ) - rule.clickOn(CommonStrings.action_manage_account_and_devices) + clickOn(CommonStrings.action_manage_account_and_devices) } } @Test - fun `when accountManagementUrl is null, item is not shown`() { + fun `when accountManagementUrl is null, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( accountManagementUrl = null, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_manage_account_and_devices)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.action_manage_account_and_devices)).assertDoesNotExist() } @Test - fun `click on Link new devices invokes the expected callback`() { + fun `click on Link new devices invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( showLinkNewDevice = true, eventSink = eventsRecorder, ), onLinkNewDeviceClick = callback, ) - rule.clickOn(CommonStrings.common_link_new_device) + clickOn(CommonStrings.common_link_new_device) } } @Test - fun `when showLinkNewDevice is false, item is not shown`() { + fun `when showLinkNewDevice is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( showLinkNewDevice = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_link_new_device)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_link_new_device)).assertDoesNotExist() } @Test - fun `click on Analytics invokes the expected callback`() { + fun `click on Analytics invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( showAnalyticsSettings = true, eventSink = eventsRecorder, ), onOpenAnalytics = callback, ) - rule.clickOn(CommonStrings.common_analytics) + clickOn(CommonStrings.common_analytics) } } @Test - fun `when showAnalyticsSettings is false, item is not shown`() { + fun `when showAnalyticsSettings is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( showAnalyticsSettings = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_analytics)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_analytics)).assertDoesNotExist() } @Test - fun `click on Report a problem invokes the expected callback`() { + fun `click on Report a problem invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( canReportBug = true, eventSink = eventsRecorder, ), onOpenRageShake = callback, ) - rule.clickOn(CommonStrings.common_report_a_problem) + clickOn(CommonStrings.common_report_a_problem) } } @Test - fun `when canReportBug is false, item is not shown`() { + fun `when canReportBug is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( canReportBug = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_report_a_problem)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_report_a_problem)).assertDoesNotExist() } @Test - fun `click on Screen lock invokes the expected callback`() { + fun `click on Screen lock invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder, ), onOpenLockScreenSettings = callback, ) - rule.clickOn(CommonStrings.common_screen_lock) + clickOn(CommonStrings.common_screen_lock) } } @Test - fun `click on About invokes the expected callback`() { + fun `click on About invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder, ), onOpenAbout = callback, ) - rule.clickOn(CommonStrings.common_about) + clickOn(CommonStrings.common_about) } } @Test - fun `click on Developer settings invokes the expected callback`() { + fun `click on Developer settings invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( showDeveloperSettings = true, eventSink = eventsRecorder, ), onOpenDeveloperSettings = callback, ) - rule.clickOn(CommonStrings.common_developer_options) + clickOn(CommonStrings.common_developer_options) } } @Test - fun `when showDeveloperSettings is false, item is not shown`() { + fun `when showDeveloperSettings is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( showDeveloperSettings = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_developer_options)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_developer_options)).assertDoesNotExist() } @Test - fun `click on Advanced settings invokes the expected callback`() { + fun `click on Advanced settings invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder, ), onOpenAdvancedSettings = callback, ) - rule.clickOn(CommonStrings.common_advanced_settings) + clickOn(CommonStrings.common_advanced_settings) } } @Test - fun `click on Labs invokes the expected callback`() { + fun `click on Labs invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( showLabsItem = true, eventSink = eventsRecorder, ), onOpenLabs = callback, ) - rule.clickOn(R.string.screen_labs_title) + clickOn(R.string.screen_labs_title) } } @Test - fun `when showLabsItem is false, item is not shown`() { + fun `when showLabsItem is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( showLabsItem = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(R.string.screen_labs_title)).assertDoesNotExist() + onNodeWithText(activity!!.getString(R.string.screen_labs_title)).assertDoesNotExist() } @Test - fun `click on Notification invokes the expected callback`() { + fun `click on Notification invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder, ), onOpenNotificationSettings = callback, ) - rule.clickOn(R.string.screen_notification_settings_title) + clickOn(R.string.screen_notification_settings_title) } } @Test - fun `click on Blocked users invokes the expected callback`() { + fun `click on Blocked users invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( nbOfBlockedUsers = 1, eventSink = eventsRecorder, ), onOpenBlockedUsers = callback, ) - rule.clickOn(CommonStrings.common_blocked_users) + clickOn(CommonStrings.common_blocked_users) } } @Test - fun `when nbOfBlockedUsers is 0, item is not shown`() { + fun `when nbOfBlockedUsers is 0, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( nbOfBlockedUsers = 0, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.common_blocked_users)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.common_blocked_users)).assertDoesNotExist() } @Test - fun `click on Remove this device invokes the expected callback`() { + fun `click on Remove this device invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( eventSink = eventsRecorder, ), onSignOutClick = callback, ) - rule.clickOn(CommonStrings.action_signout) + clickOn(CommonStrings.action_signout) } } @Test - fun `click on Deactivate invokes the expected callback`() { + fun `click on Deactivate invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setView( + setView( aPreferencesRootState( canDeactivateAccount = true, eventSink = eventsRecorder, ), onDeactivateClick = callback, ) - rule.clickOn(CommonStrings.action_delete_account) + clickOn(CommonStrings.action_delete_account) } } @Test - fun `when canDeactivateAccount is false, item is not shown`() { + fun `when canDeactivateAccount is false, item is not shown`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setView( + setView( aPreferencesRootState( canDeactivateAccount = false, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_delete_account)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.action_delete_account)).assertDoesNotExist() } @Test - fun `clicking on version sends a PreferencesRootEvents`() { + fun `clicking on version sends a PreferencesRootEvents`() = runAndroidComposeUiTest { val version = "VERSION" val eventsRecorder = EventsRecorder() - rule.setView( + setView( aPreferencesRootState( version = version, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(version).performClick() + onNodeWithText(version).performClick() eventsRecorder.assertSingle(PreferencesRootEvent.OnVersionInfoClick) } } -private fun AndroidComposeTestRule.setView( +private fun AndroidComposeUiTest.setView( state: PreferencesRootState, onBackClick: () -> Unit = EnsureNeverCalled(), onAddAccountClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileViewTest.kt index 728e05ee7e..20db955955 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.preferences.impl.user.editprofile import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -23,96 +26,93 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class EditUserProfileViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back emits the expected event`() { + fun `clicking on back emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( eventSink = eventsRecorder, ), ) - rule.pressBack() + pressBack() eventsRecorder.assertSingle(EditUserProfileEvent.Exit) } @Test - fun `clicking on save from the exit confirmation dialog emits the expected event`() { + fun `clicking on save from the exit confirmation dialog emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save, inDialog = true) + clickOn(CommonStrings.action_save, inDialog = true) eventsRecorder.assertSingle(EditUserProfileEvent.Save) } @Test - fun `clicking on discard exit emits the expected event`() { + fun `clicking on discard exit emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_discard) + clickOn(CommonStrings.action_discard) eventsRecorder.assertSingle(EditUserProfileEvent.Exit) } @Test - fun `clicking on save emits the expected event`() { + fun `clicking on save emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( saveButtonEnabled = true, saveAction = AsyncAction.Uninitialized, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertSingle(EditUserProfileEvent.Save) } @Test - fun `clicking on avatar opens the bottom sheet dialog`() { + fun `clicking on avatar opens the bottom sheet dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val actions = listOf( AvatarAction.TakePhoto, AvatarAction.ChoosePhoto, AvatarAction.Remove, ) - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( saveAction = AsyncAction.Uninitialized, avatarActions = actions, eventSink = eventsRecorder, ), ) - val contentDescription = rule.activity.getString(CommonStrings.a11y_avatar) - rule.onNodeWithContentDescription(contentDescription).performClick() + val resources = activity!!.resources + val contentDescription = resources.getString(CommonStrings.a11y_avatar) + onNodeWithContentDescription(contentDescription).performClick() // Assert that the actions are displayed actions.forEach { action -> - val text = rule.activity.getString(action.titleResId) - rule.onNodeWithText(text).assertExists() + val text = resources.getString(action.titleResId) + onNodeWithText(text).assertExists() } } @Test - fun `success invokes the expected callback`() { + fun `success invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setEditUserProfileView( + setEditUserProfileView( aEditUserProfileState( saveAction = AsyncAction.Success(Unit), eventSink = eventsRecorder, @@ -123,7 +123,7 @@ class EditUserProfileViewTest { } } -private fun AndroidComposeTestRule.setEditUserProfileView( +private fun AndroidComposeUiTest.setEditUserProfileView( state: EditUserProfileState, onEditProfileSuccess: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt index 59d9507571..a18c82b275 100644 --- a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt +++ b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/ReportRoomViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.reportroom.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -20,76 +23,72 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ReportRoomViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invoke the expected callback`() { + fun `clicking on back invoke the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setReportRoomView( + setReportRoomView( aReportRoomState( eventSink = eventsRecorder, ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on report when enabled emits the expected event`() { + fun `clicking on report when enabled emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setReportRoomView( + setReportRoomView( aReportRoomState( reason = "Spam", eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_report) + clickOn(CommonStrings.action_report) eventsRecorder.assertSingle(ReportRoomEvents.Report) } @Test - fun `clicking on decline when disabled does not emit event`() { + fun `clicking on decline when disabled does not emit event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setReportRoomView( + setReportRoomView( aReportRoomState(eventSink = eventsRecorder), ) - rule.clickOn(CommonStrings.action_report) + clickOn(CommonStrings.action_report) } @Test - fun `clicking on leave room option emits the expected event`() { + fun `clicking on leave room option emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setReportRoomView( + setReportRoomView( aReportRoomState(eventSink = eventsRecorder), ) - rule.clickOn(CommonStrings.action_leave_room) + clickOn(CommonStrings.action_leave_room) eventsRecorder.assertSingle(ReportRoomEvents.ToggleLeaveRoom) } @Test - fun `typing text in the reason field emits the expected Event`() { + fun `typing text in the reason field emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setReportRoomView( + setReportRoomView( aReportRoomState( eventSink = eventsRecorder, reason = "" ), ) - rule.onNodeWithText("").performTextInput("Spam!") + onNodeWithText("").performTextInput("Spam!") eventsRecorder.assertSingle(ReportRoomEvents.UpdateReason("Spam!")) } } -private fun AndroidComposeTestRule.setReportRoomView( +private fun AndroidComposeUiTest.setReportRoomView( state: ReportRoomState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt index f28c9c150f..668c6bb221 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.rolesandpermissions.impl.permissions import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -23,84 +26,80 @@ import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressBackKey import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ChangeRoomPermissionsViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `click on back icon invokes Exit`() { + fun `click on back icon invokes Exit`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( eventSink = recorder ) ) - rule.pressBack() + pressBack() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) } @Test - fun `click on back key invokes Exit`() { + fun `click on back key invokes Exit`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( eventSink = recorder ) ) - rule.pressBackKey() + pressBackKey() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) } @Test - fun `when confirming exit with pending changes, using the back key actually exits`() { + fun `when confirming exit with pending changes, using the back key actually exits`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, eventSink = recorder, ), ) - rule.pressBackKey() + pressBackKey() recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) } @Test - fun `when confirming exit with pending changes, clicking on 'discard' button in the dialog actually exits`() { + fun `when confirming exit with pending changes, clicking on 'discard' button in the dialog actually exits`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, saveAction = AsyncAction.ConfirmingCancellation, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_discard) + clickOn(CommonStrings.action_discard) recorder.assertSingle(ChangeRoomPermissionsEvent.Exit) } @Test - fun `when confirming exit with pending changes, clicking on 'save' button in the dialog saves the changes`() { + fun `when confirming exit with pending changes, clicking on 'save' button in the dialog saves the changes`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, saveAction = AsyncAction.ConfirmingCancellation, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_save, inDialog = true) + clickOn(CommonStrings.action_save, inDialog = true) recorder.assertSingle(ChangeRoomPermissionsEvent.Save) } @Test - fun `click on a role item triggers ChangeRole event`() { + fun `click on a role item triggers ChangeRole event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( itemsBySection = persistentMapOf( // Makes sure there is only one item to click on @@ -109,70 +108,70 @@ class ChangeRoomPermissionsViewTest { eventSink = recorder, ) ) - rule.clickOn(R.string.screen_room_change_permissions_room_name) - rule.clickOn(R.string.screen_room_change_permissions_everyone) + clickOn(R.string.screen_room_change_permissions_room_name) + clickOn(R.string.screen_room_change_permissions_everyone) recorder.assertSingle( ChangeRoomPermissionsEvent.ChangeMinimumRoleForAction(RoomPermissionType.ROOM_NAME, SelectableRole.Everyone), ) } @Test - fun `click on the Save menu item triggers Save event`() { + fun `click on the Save menu item triggers Save event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) recorder.assertSingle(ChangeRoomPermissionsEvent.Save) } @Test - fun `a successful save exits the screen`() { + fun `a successful save exits the screen`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(true) { callback -> - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, saveAction = AsyncAction.Success(true), ), onComplete = callback, ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) } } @Test - fun `a cancellation exits the screen`() { + fun `a cancellation exits the screen`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(false) { callback -> - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, saveAction = AsyncAction.Success(false), ), onComplete = callback, ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) } } @Test - fun `click on the Ok option in save error dialog triggers ResetPendingAction event`() { + fun `click on the Ok option in save error dialog triggers ResetPendingAction event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setChangeRoomPermissionsRule( + setChangeRoomPermissionsRule( state = aChangeRoomPermissionsState( hasChanges = true, saveAction = AsyncAction.Failure(IllegalStateException("Failed to set room power levels")), eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) recorder.assertSingle(ChangeRoomPermissionsEvent.ResetPendingActions) } } -private fun AndroidComposeTestRule.setChangeRoomPermissionsRule( +private fun AndroidComposeUiTest.setChangeRoomPermissionsRule( state: ChangeRoomPermissionsState = aChangeRoomPermissionsState(), onComplete: (Boolean) -> Unit = EnsureNeverCalledWithParam(), ) { diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt index 09bef49cbd..62d0608f26 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/roles/ChangeRolesViewTest.kt @@ -6,15 +6,18 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.rolesandpermissions.impl.roles import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncAction @@ -30,20 +33,16 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.pressBackKey import kotlinx.collections.immutable.toImmutableList -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class ChangeRolesViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `passing a 'User' role throws an exception`() { + fun `passing a 'User' role throws an exception`() = runAndroidComposeUiTest { val exception = runCatchingExceptions { - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( role = RoomMember.Role.User, eventSink = EnsureNeverCalledWithParam(), @@ -54,106 +53,106 @@ class ChangeRolesViewTest { } @Test - fun `back key - with search active toggles the search`() { + fun `back key - with search active toggles the search`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, eventSink = eventsRecorder, ), ) - rule.pressBackKey() + pressBackKey() // Advance time to let the event be processed, as the search toggle might have some delay (e.g. for the animation) - rule.mainClock.advanceTimeBy(1) + mainClock.advanceTimeBy(1) eventsRecorder.assertSingle(ChangeRolesEvent.ToggleSearchActive) } @Test - fun `back key - with search inactive exits the screen`() { + fun `back key - with search inactive exits the screen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = false, eventSink = eventsRecorder, ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(ChangeRolesEvent.Exit) } @Test - fun `back button - exits the screen`() { + fun `back button - exits the screen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = false, eventSink = eventsRecorder, ), ) - rule.pressBack() + pressBack() eventsRecorder.assertSingle(ChangeRolesEvent.Exit) } @Test - fun `save button - with changes, it saves them`() { + fun `save button - with changes, it saves them`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( hasPendingChanges = true, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertSingle(ChangeRolesEvent.Save) } @Test - fun `save button - with no changes, does nothing`() { + fun `save button - with no changes, does nothing`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( hasPendingChanges = false, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertEmpty() } @Test - fun `exit confirmation dialog - discard exits the screen`() { + fun `exit confirmation dialog - discard exits the screen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, savingState = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_discard) + clickOn(CommonStrings.action_discard) eventsRecorder.assertSingle(ChangeRolesEvent.Exit) } @Test - fun `exit confirmation dialog - save emits the save event`() { + fun `exit confirmation dialog - save emits the save event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, savingState = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertSingle(ChangeRolesEvent.Save) } @Test - fun `save admins confirmation dialog - submit saves the changes`() { + fun `save admins confirmation dialog - submit saves the changes`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( role = RoomMember.Role.Admin, isSearchActive = true, @@ -161,14 +160,14 @@ class ChangeRolesViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(ChangeRolesEvent.Save) } @Test - fun `save owners confirmation dialog - continue saves the changes`() { + fun `save owners confirmation dialog - continue saves the changes`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( role = RoomMember.Role.Owner(isCreator = false), isSearchActive = true, @@ -176,14 +175,14 @@ class ChangeRolesViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(ChangeRolesEvent.Save) } @Test - fun `save admins confirmation dialog - cancel removes the dialog`() { + fun `save admins confirmation dialog - cancel removes the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( role = RoomMember.Role.Admin, isSearchActive = true, @@ -191,14 +190,14 @@ class ChangeRolesViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog) } @Test - fun `save owners confirmation dialog - cancel removes the dialog`() { + fun `save owners confirmation dialog - cancel removes the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( role = RoomMember.Role.Owner(isCreator = false), isSearchActive = true, @@ -206,39 +205,39 @@ class ChangeRolesViewTest { eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog) } @Test - fun `error dialog - dismissing removes the dialog`() { + fun `error dialog - dismissing removes the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesState( isSearchActive = true, savingState = AsyncAction.Failure(IllegalStateException("boom")), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(ChangeRolesEvent.CloseDialog) } @Test - fun `testing removing user from selected list emits the expected event`() { + fun `testing removing user from selected list emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val selectedUsers = aMatrixUserList().take(2) val userToDeselect = selectedUsers[1] assertThat(userToDeselect.displayName).isEqualTo("Bob") - rule.setChangeRolesContent( + setChangeRolesContent( state = aChangeRolesStateWithSelectedUsers().copy( selectedUsers = selectedUsers.toImmutableList(), eventSink = eventsRecorder, ), ) // Unselect the user from the row list - val contentDescription = rule.activity.getString(CommonStrings.action_remove) - rule.onNodeWithContentDescription( + val contentDescription = activity!!.getString(CommonStrings.action_remove) + onNodeWithContentDescription( label = contentDescription, useUnmergedTree = true, ).performClick() @@ -247,7 +246,7 @@ class ChangeRolesViewTest { @Test @Config(qualifiers = "h1000dp") - fun `testing adding user to the selected list emits the expected event`() { + fun `testing adding user to the selected list emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val selectedUsers = aMatrixUserList().take(2) val state = aChangeRolesStateWithSelectedUsers().copy( @@ -256,16 +255,16 @@ class ChangeRolesViewTest { ) val userToSelect = (state.searchResults as SearchBarResultState.Results).results.members.first().toMatrixUser() assertThat(userToSelect.displayName).isEqualTo("Carol") - rule.setChangeRolesContent( + setChangeRolesContent( state = state, ) // Select the user from the user list - rule.onNodeWithText("Carol").performClick() + onNodeWithText("Carol").performClick() eventsRecorder.assertSingle(ChangeRolesEvent.UserSelectionToggled(userToSelect)) } @Test - fun `testing removing user to the selected list emits the expected event`() { + fun `testing removing user to the selected list emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val selectedUsers = aMatrixUserList().take(2) val state = aChangeRolesStateWithSelectedUsers().copy( @@ -274,18 +273,18 @@ class ChangeRolesViewTest { ) val userToSelect = (state.searchResults as SearchBarResultState.Results).results.moderators.first().toMatrixUser() assertThat(userToSelect.displayName).isEqualTo("Bob") - rule.setChangeRolesContent( + setChangeRolesContent( state = state, ) // Unselect the user from the user list - rule.onAllNodesWithText( + onAllNodesWithText( text = "Bob", useUnmergedTree = true, )[1].performClick() eventsRecorder.assertSingle(ChangeRolesEvent.UserSelectionToggled(userToSelect)) } - private fun AndroidComposeTestRule.setChangeRolesContent( + private fun AndroidComposeUiTest.setChangeRolesContent( state: ChangeRolesState, ) { setContent { diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt index e08ae205b7..d8908c405d 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.rolesandpermissions.impl.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.rolesandpermissions.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -23,159 +26,154 @@ import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledTimes import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.setSafeContent -import kotlinx.coroutines.test.runTest -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class RolesAndPermissionsViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `click on back invokes expected callback`() { + fun `click on back invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( goBack = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `tapping on Admins opens admin list`() { + fun `tapping on Admins opens admin list`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( aRolesAndPermissionsState( roomSupportsOwners = false, eventSink = EventsRecorder(expectEvents = false) ), openAdminList = callback, ) - rule.clickOn(R.string.screen_room_roles_and_permissions_admins) + clickOn(R.string.screen_room_roles_and_permissions_admins) } } @Test - fun `tapping on Admins and Owners opens admin list`() { + fun `tapping on Admins and Owners opens admin list`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( aRolesAndPermissionsState( roomSupportsOwners = true, eventSink = EventsRecorder(expectEvents = false) ), openAdminList = callback, ) - rule.clickOn(R.string.screen_room_roles_and_permissions_admins_and_owners) + clickOn(R.string.screen_room_roles_and_permissions_admins_and_owners) } } @Test - fun `tapping on Moderators opens moderators list`() { + fun `tapping on Moderators opens moderators list`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( openModeratorList = callback, ) - rule.clickOn(R.string.screen_room_roles_and_permissions_moderators) + clickOn(R.string.screen_room_roles_and_permissions_moderators) } } @Test @Config(qualifiers = "h640dp") - fun `tapping permission item open the change permissions screen`() { + fun `tapping permission item open the change permissions screen`() = runAndroidComposeUiTest { ensureCalledTimes(1) { callback -> - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( openEditPermissions = callback, ) - rule.clickOn(R.string.screen_room_roles_and_permissions_permissions_header) + clickOn(R.string.screen_room_roles_and_permissions_permissions_header) } } @Test @Config(qualifiers = "h640dp") - fun `tapping on reset permissions triggers ResetPermissions event`() { + fun `tapping on reset permissions triggers ResetPermissions event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( eventSink = recorder, ), ) - rule.clickOn(R.string.screen_room_roles_and_permissions_reset) + clickOn(R.string.screen_room_roles_and_permissions_reset) recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions) } @Test - fun `tapping on Reset in the reset permissions confirmation dialog triggers ResetPermissions event`() { + fun `tapping on Reset in the reset permissions confirmation dialog triggers ResetPermissions event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( resetPermissionsAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_reset) + clickOn(CommonStrings.action_reset) recorder.assertSingle(RolesAndPermissionsEvents.ResetPermissions) } @Test - fun `tapping on Cancel in the reset permissions confirmation dialog triggers CancelPendingAction event`() { + fun `tapping on Cancel in the reset permissions confirmation dialog triggers CancelPendingAction event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( resetPermissionsAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction) } @Test - fun `tapping on 'Demote to moderator' in the demote self bottom sheet triggers the right event`() { + fun `tapping on 'Demote to moderator' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) - rule.clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator) - rule.mainClock.advanceTimeBy(1_000L) + clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_moderator) + mainClock.advanceTimeBy(1_000L) recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.Moderator)) } @Test - fun `tapping on 'Demote to member' in the demote self bottom sheet triggers the right event`() = runTest { + fun `tapping on 'Demote to member' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) - rule.clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_member) - rule.mainClock.advanceTimeBy(1_000L) + clickOn(R.string.screen_room_roles_and_permissions_change_role_demote_to_member) + mainClock.advanceTimeBy(1_000L) recorder.assertSingle(RolesAndPermissionsEvents.DemoteSelfTo(RoomMember.Role.User)) } @Test - fun `tapping on 'Cancel' in the demote self bottom sheet triggers the right event`() { + fun `tapping on 'Cancel' in the demote self bottom sheet triggers the right event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setRolesAndPermissionsView( + setRolesAndPermissionsView( state = aRolesAndPermissionsState( changeOwnRoleAction = AsyncAction.ConfirmingNoParams, eventSink = recorder, ), ) - rule.clickOn(CommonStrings.action_cancel) - rule.mainClock.advanceTimeBy(1_000L) + clickOn(CommonStrings.action_cancel) + mainClock.advanceTimeBy(1_000L) recorder.assertSingle(RolesAndPermissionsEvents.CancelPendingAction) } } -private fun AndroidComposeTestRule.setRolesAndPermissionsView( +private fun AndroidComposeUiTest.setRolesAndPermissionsView( state: RolesAndPermissionsState = aRolesAndPermissionsState( roomSupportsOwners = false, eventSink = EventsRecorder(expectEvents = false), diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt index 4b37f993f9..5f871183d6 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasHelperViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.roomaliasresolver.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @@ -22,48 +25,44 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomAliasHelperViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setRoomAliasResolverView( + setRoomAliasResolverView( aRoomAliasResolverState( eventSink = eventsRecorder, ), onBackClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on Retry emits the expected Event`() { + fun `clicking on Retry emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomAliasResolverView( + setRoomAliasResolverView( aRoomAliasResolverState( resolveState = AsyncData.Failure(Exception("Error")), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_retry) + clickOn(CommonStrings.action_retry) eventsRecorder.assertSingle(RoomAliasResolverEvents.Retry) } @Test - fun `success state invokes the expected Callback`() { + fun `success state invokes the expected Callback`() = runAndroidComposeUiTest { val result = aResolvedRoomAlias() val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(result) { - rule.setRoomAliasResolverView( + setRoomAliasResolverView( aRoomAliasResolverState( resolveState = AsyncData.Success(result), eventSink = eventsRecorder, @@ -74,7 +73,7 @@ class RoomAliasHelperViewTest { } } -private fun AndroidComposeTestRule.setRoomAliasResolverView( +private fun AndroidComposeUiTest.setRoomAliasResolverView( state: RoomAliasResolverState, onBackClick: () -> Unit = EnsureNeverCalled(), onAliasResolved: (ResolvedRoomAlias) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 588a10a218..50139e0149 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.roomdetails.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.userprofile.shared.aUserProfileState @@ -32,98 +35,94 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class RoomDetailsViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `click on back invokes expected callback`() { + fun `click on back invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( goBack = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `click on share invokes expected callback`() { + fun `click on share invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( onShareRoom = callback, ) - rule.clickOn(CommonStrings.action_share) + clickOn(CommonStrings.action_share) } } @Config(qualifiers = "h1024dp") @Test - fun `click on room members invokes expected callback`() { + fun `click on room members invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( openRoomMemberList = callback, ) - rule.clickOn(CommonStrings.common_people) + clickOn(CommonStrings.common_people) } } @Config(qualifiers = "h1024dp") @Test - fun `click on polls invokes expected callback`() { + fun `click on polls invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( openPollHistory = callback, ) - rule.clickOn(R.string.screen_polls_history_title) + clickOn(R.string.screen_polls_history_title) } } @Config(qualifiers = "h1024dp") @Test - fun `click on media gallery invokes expected callback`() { + fun `click on media gallery invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( openMediaGallery = callback, ) - rule.clickOn(R.string.screen_room_details_media_gallery_title) + clickOn(R.string.screen_room_details_media_gallery_title) } } @Config(qualifiers = "h1024dp") @Test - fun `click on notification invokes expected callback`() { + fun `click on notification invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( openRoomNotificationSettings = callback, ) - rule.clickOn(R.string.screen_room_details_notification_title) + clickOn(R.string.screen_room_details_notification_title) } } @Test - fun `click on invite invokes expected callback`() { + fun `click on invite invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canInvite = true, ), invitePeople = callback, ) - rule.clickOn(CommonStrings.action_invite) + clickOn(CommonStrings.action_invite) } } @Test - fun `click on call invokes expected callback`() { + fun `click on call invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(CallIntent.AUDIO) { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canInvite = true, @@ -134,103 +133,103 @@ class RoomDetailsViewTest { ), onJoinCallClick = callback, ) - rule.clickOn(CommonStrings.action_call) + clickOn(CommonStrings.action_call) } } @Test - fun `click on video call invokes expected callback`() { + fun `click on video call invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(CallIntent.VIDEO) { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canInvite = true, ), onJoinCallClick = callback, ) - rule.clickOn(CommonStrings.common_video) + clickOn(CommonStrings.common_video) } } @Config(qualifiers = "h1024dp") @Test - fun `click on pinned messages invokes expected callback`() { + fun `click on pinned messages invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canInvite = true, ), onPinnedMessagesClick = callback, ) - rule.clickOn(R.string.screen_room_details_pinned_events_row_title) + clickOn(R.string.screen_room_details_pinned_events_row_title) } } @Config(qualifiers = "h1024dp") @Test - fun `click on security and privacy invokes expected callback`() { + fun `click on security and privacy invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canShowSecurityAndPrivacy = true, ), onSecurityAndPrivacyClick = callback, ) - rule.clickOn(R.string.screen_room_details_security_and_privacy_title) + clickOn(R.string.screen_room_details_security_and_privacy_title) } } @Config(qualifiers = "h1024dp") @Test - fun `click on add topic emit expected event`() { + fun `click on add topic emit expected event`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(RoomDetailsAction.AddTopic) { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), roomTopic = RoomTopicState.CanAddTopic, ), onActionClick = callback, ) - rule.clickOn(R.string.screen_room_details_add_topic_title) + clickOn(R.string.screen_room_details_add_topic_title) } } @Test - fun `click on menu edit emit expected event`() { + fun `click on menu edit emit expected event`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(RoomDetailsAction.Edit) { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canEdit = true, ), onActionClick = callback, ) - val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu) - rule.onNodeWithContentDescription(menuContentDescription).performClick() - rule.clickOn(CommonStrings.action_edit) + val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) + onNodeWithContentDescription(menuContentDescription).performClick() + clickOn(CommonStrings.action_edit) } } @Test - fun `click on avatar test`() { + fun `click on avatar test`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aRoomDetailsState( eventSink = eventsRecorder, roomAvatarUrl = "an_avatar_url", ) val callback = EnsureCalledOnceWithTwoParams(state.roomName, "an_avatar_url") - rule.setRoomDetailView( + setRoomDetailView( state = state, openAvatarPreview = callback, ) - rule.onNodeWithTag(TestTags.roomDetailAvatar.value).performClick() + onNodeWithTag(TestTags.roomDetailAvatar.value).performClick() callback.assertSuccess() } @Test - fun `click on avatar test on DM`() { + fun `click on avatar test on DM`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aRoomDetailsState( roomType = RoomDetailsType.Dm( @@ -241,114 +240,114 @@ class RoomDetailsViewTest { eventSink = eventsRecorder, ) val callback = EnsureCalledOnceWithTwoParams("Daniel", "an_avatar_url") - rule.setRoomDetailView( + setRoomDetailView( state = state, openAvatarPreview = callback, ) - rule.onNodeWithTag(TestTags.memberDetailAvatar.value).performClick() + onNodeWithTag(TestTags.memberDetailAvatar.value).performClick() callback.assertSuccess() } @Test - fun `click on mute emit expected event`() { + fun `click on mute emit expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomDetailsState( eventSink = eventsRecorder, roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.ALL_MESSAGES), ) - rule.setRoomDetailView( + setRoomDetailView( state = state, ) - rule.clickOn(CommonStrings.common_mute) + clickOn(CommonStrings.common_mute) eventsRecorder.assertSingle(RoomDetailsEvent.MuteNotification) } @Test - fun `click on unmute emit expected event`() { + fun `click on unmute emit expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomDetailsState( eventSink = eventsRecorder, roomNotificationSettings = aRoomNotificationSettings(mode = RoomNotificationMode.MUTE), ) - rule.setRoomDetailView( + setRoomDetailView( state = state, ) - rule.clickOn(CommonStrings.common_unmute) + clickOn(CommonStrings.common_unmute) eventsRecorder.assertSingle(RoomDetailsEvent.UnmuteNotification) } @Config(qualifiers = "h1024dp") @Test - fun `click on favorite emit expected Event`() { + fun `click on favorite emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.common_favourite) + clickOn(CommonStrings.common_favourite) eventsRecorder.assertSingle(RoomDetailsEvent.SetFavorite(true)) } @Config(qualifiers = "h1500dp") @Test - fun `click on leave emit expected Event`() { + fun `click on leave emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_room_details_leave_room_title) + clickOn(R.string.screen_room_details_leave_room_title) eventsRecorder.assertSingle(RoomDetailsEvent.LeaveRoom(needsConfirmation = true)) } @Config(qualifiers = "h1500dp") @Test - fun `click on report room invokes expected callback`() { + fun `click on report room invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), ), onReportRoomClick = callback, ) - rule.clickOn(CommonStrings.action_report_room) + clickOn(CommonStrings.action_report_room) } } @Config(qualifiers = "h1024dp") @Test - fun `click on knock requests invokes expected callback`() { + fun `click on knock requests invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canShowKnockRequests = true, ), onKnockRequestsClick = callback, ) - rule.clickOn(R.string.screen_room_details_requests_to_join_title) + clickOn(R.string.screen_room_details_requests_to_join_title) } } @Config(qualifiers = "h1024dp") @Test - fun `click on profile invokes the expected callback`() { + fun `click on profile invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(A_USER_ID) { callback -> - rule.setRoomDetailView( + setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), roomMemberDetailsState = aUserProfileState(userId = A_USER_ID), ), onProfileClick = callback, ) - rule.clickOn(R.string.screen_room_details_profile_row_title) + clickOn(R.string.screen_room_details_profile_row_title) } } } -private fun AndroidComposeTestRule.setRoomDetailView( +private fun AndroidComposeUiTest.setRoomDetailView( state: RoomDetailsState = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), ), diff --git a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditViewTest.kt b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditViewTest.kt index 71fb143074..686794d641 100644 --- a/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditViewTest.kt +++ b/features/roomdetailsedit/impl/src/test/kotlin/io/element/android/features/roomdetailsedit/impl/RoomDetailsEditViewTest.kt @@ -5,18 +5,21 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.roomdetailsedit.impl import androidx.activity.ComponentActivity import androidx.annotation.StringRes +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assert import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.isEditable -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -28,58 +31,54 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import org.junit.Ignore -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomDetailsEditViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back emits the expected Event`() { + fun `clicking on back emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder ), ) - rule.pressBack() + pressBack() eventsRecorder.assertSingle(RoomDetailsEditEvent.OnBackPress) } @Test - fun `clicking on discard when confirming exit emits the expected Event`() { + fun `clicking on discard when confirming exit emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_discard) + clickOn(CommonStrings.action_discard) eventsRecorder.assertSingle(RoomDetailsEditEvent.OnBackPress) } @Test - fun `clicking on save when confirming exit emits the expected Event`() { + fun `clicking on save when confirming exit emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save, inDialog = true) + clickOn(CommonStrings.action_save, inDialog = true) eventsRecorder.assertSingle(RoomDetailsEditEvent.Save) } @Test - fun `when edition is successful, the expected callback is invoked`() { + fun `when edition is successful, the expected callback is invoked`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, saveAction = AsyncAction.Success(Unit) @@ -90,55 +89,55 @@ class RoomDetailsEditViewTest { } @Test - fun `when name is changed, the expected Event is emitted`() { + fun `when name is changed, the expected Event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, roomRawName = "Marketing", ), ) - rule.onNodeWithText("Marketing").performTextInput("A") + onNodeWithText("Marketing").performTextInput("A") eventsRecorder.assertSingle(RoomDetailsEditEvent.UpdateRoomName("AMarketing")) } @Test - fun `when user cannot change name, nothing happen`() { + fun `when user cannot change name, nothing happen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, roomRawName = "Marketing", canChangeName = false, ), ) - rule.onNodeWithText("Marketing").assert(!isEditable()) + onNodeWithText("Marketing").assert(!isEditable()) } @Test - fun `when topic is changed, the expected Event is emitted`() { + fun `when topic is changed, the expected Event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, roomTopic = "My Topic", ), ) - rule.onNodeWithText("My Topic").performTextInput("A") + onNodeWithText("My Topic").performTextInput("A") eventsRecorder.assertSingle(RoomDetailsEditEvent.UpdateRoomTopic("AMy Topic")) } @Test - fun `when user cannot change topic, nothing happen`() { + fun `when user cannot change topic, nothing happen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, roomTopic = "My Topic", canChangeTopic = false, ), ) - rule.onNodeWithText("My Topic").assert(!isEditable()) + onNodeWithText("My Topic").assert(!isEditable()) } @Ignore("This test is failing because the bottom sheet does not open") @@ -171,73 +170,73 @@ class RoomDetailsEditViewTest { private fun testAvatarChange( @StringRes stringActionRes: Int, expectedEvent: RoomDetailsEditEvent.HandleAvatarAction, - ) { + ) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, ), ) // Open the bottom sheet - rule.onNode(hasTestTag(TestTags.editAvatar.value)).performClick() - rule.onNodeWithText(rule.activity.getString(stringActionRes)).assertExists() - rule.clickOn(stringActionRes) + onNode(hasTestTag(TestTags.editAvatar.value)).performClick() + onNodeWithText(activity!!.getString(stringActionRes)).assertExists() + clickOn(stringActionRes) eventsRecorder.assertSingle(expectedEvent) } @Test - fun `when user cannot change avatar, nothing happen`() { + fun `when user cannot change avatar, nothing happen`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, canChangeAvatar = false, ), ) - rule.onNode(hasTestTag(TestTags.editAvatar.value)).performClick() - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_take_photo)).assertDoesNotExist() + onNode(hasTestTag(TestTags.editAvatar.value)).performClick() + onNodeWithText(activity!!.getString(CommonStrings.action_take_photo)).assertDoesNotExist() } @Test - fun `when save is clicked, the expected Event is emitted`() { + fun `when save is clicked, the expected Event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, saveButtonEnabled = true, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertSingle(RoomDetailsEditEvent.Save) } @Test - fun `when save is clicked, but nothing need to be saved, nothing happens`() { + fun `when save is clicked, but nothing need to be saved, nothing happens`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, saveButtonEnabled = false, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) } @Test - fun `when error is shown, closing the dialog emit the expected Event`() { + fun `when error is shown, closing the dialog emit the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDetailsEditView( + setRoomDetailsEditView( aRoomDetailsEditState( eventSink = eventsRecorder, saveAction = AsyncAction.Failure(RuntimeException("Whelp")), ), ) - rule.clickOn(CommonStrings.action_ok) + clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(RoomDetailsEditEvent.CloseDialog) } } -private fun AndroidComposeTestRule.setRoomDetailsEditView( +private fun AndroidComposeUiTest.setRoomDetailsEditView( state: RoomDetailsEditState, onDone: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt index a50ad6a22c..f9d60f87da 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryViewTest.kt @@ -6,15 +6,18 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.roomdirectory.impl.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.testtags.TestTags @@ -22,31 +25,27 @@ import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.ensureCalledOnceWithParam -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomDirectoryViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `typing text in search field emits the expected Event`() { + fun `typing text in search field emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomDirectoryView( + setRoomDirectoryView( state = aRoomDirectoryState( eventSink = eventsRecorder, ) ) - rule.onNodeWithTag(TestTags.searchTextField.value).performTextInput( + onNodeWithTag(TestTags.searchTextField.value).performTextInput( text = "Test" ) eventsRecorder.assertSingle(RoomDirectoryEvents.Search("Test")) } @Test - fun `clicking on room item then onResultClick lambda is called once`() { + fun `clicking on room item then onResultClick lambda is called once`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomDirectoryState( roomDescriptions = aRoomDescriptionList(), @@ -54,27 +53,27 @@ class RoomDirectoryViewTest { ) val clickedRoom = state.roomDescriptions.first() ensureCalledOnceWithParam(clickedRoom) { callback -> - rule.setRoomDirectoryView( + setRoomDirectoryView( state = state, onResultClick = callback, ) - rule.onNodeWithText(clickedRoom.computedName).performClick() + onNodeWithText(clickedRoom.computedName).performClick() } } @Test - fun `composing load more indicator emits expected Event`() { + fun `composing load more indicator emits expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aRoomDirectoryState( displayLoadMoreIndicator = true, eventSink = eventsRecorder, ) - rule.setRoomDirectoryView(state = state) + setRoomDirectoryView(state = state) eventsRecorder.assertSingle(RoomDirectoryEvents.LoadMore) } } -private fun AndroidComposeTestRule.setRoomDirectoryView( +private fun AndroidComposeUiTest.setRoomDirectoryView( state: RoomDirectoryState, onBackClick: () -> Unit = EnsureNeverCalled(), onResultClick: (RoomDescription) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt index 6508b28053..646481715a 100644 --- a/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt +++ b/features/roommembermoderation/impl/src/test/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.roommembermoderation.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.ModerationActionState @@ -24,21 +27,17 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnceWithTwoParams import io.element.android.tests.testutils.pressTag import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RoomMemberModerationViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on display profile action calls onSelectAction`() { + fun `clicking on display profile action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.DisplayProfile, user) { callback -> - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = user, actions = listOf( @@ -48,16 +47,16 @@ class RoomMemberModerationViewTest { ), onSelectAction = callback ) - rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_member_user_info) + clickOn(R.string.screen_bottom_sheet_manage_room_member_member_user_info) } } @Test - fun `clicking on kick user action calls onSelectAction`() { + fun `clicking on kick user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.KickUser, user) { callback -> - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = user, actions = listOf( @@ -67,18 +66,18 @@ class RoomMemberModerationViewTest { ), onSelectAction = callback ) - rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_remove) + clickOn(R.string.screen_bottom_sheet_manage_room_member_remove) // Gives time for bottomsheet to hide - rule.mainClock.advanceTimeBy(1_000) + mainClock.advanceTimeBy(1_000) } } @Test - fun `clicking on ban user action calls onSelectAction`() { + fun `clicking on ban user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.BanUser, user) { callback -> - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = user, actions = listOf( @@ -88,18 +87,18 @@ class RoomMemberModerationViewTest { ), onSelectAction = callback ) - rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_ban) + clickOn(R.string.screen_bottom_sheet_manage_room_member_ban) // Gives time for bottomsheet to hide - rule.mainClock.advanceTimeBy(1_000) + mainClock.advanceTimeBy(1_000) } } @Test - fun `clicking on unban user action calls onSelectAction`() { + fun `clicking on unban user action calls onSelectAction`() = runAndroidComposeUiTest { val user = anAlice() val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithTwoParams(ModerationAction.UnbanUser, user) { callback -> - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = user, actions = listOf( @@ -109,100 +108,100 @@ class RoomMemberModerationViewTest { ), onSelectAction = callback ) - rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_unban) + clickOn(R.string.screen_bottom_sheet_manage_room_member_unban) // Gives time for bottomsheet to hide - rule.mainClock.advanceTimeBy(1_000) + mainClock.advanceTimeBy(1_000) } } @Test - fun `clicking submit on kick confirmation dialog sends DoKickUser event`() { + fun `clicking submit on kick confirmation dialog sends DoKickUser event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), kickUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoKickUser(reason = "")) } @Test - fun `clicking dismiss on kick confirmation dialog sends Reset event`() { + fun `clicking dismiss on kick confirmation dialog sends Reset event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), kickUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogNegative.value) + pressTag(TestTags.dialogNegative.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) } @Test - fun `clicking submit on ban confirmation dialog sends DoBanUser event`() { + fun `clicking submit on ban confirmation dialog sends DoBanUser event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), banUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoBanUser(reason = "")) } @Test - fun `clicking dismiss on ban confirmation dialog sends Reset event`() { + fun `clicking dismiss on ban confirmation dialog sends Reset event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), banUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogNegative.value) + pressTag(TestTags.dialogNegative.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) } @Test - fun `clicking confirm on unban confirmation dialog sends DoUnbanUser event`() { + fun `clicking confirm on unban confirmation dialog sends DoUnbanUser event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogPositive.value) + pressTag(TestTags.dialogPositive.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.DoUnbanUser("")) } @Test - fun `clicking dismiss on unban confirmation dialog sends Reset event`() { + fun `clicking dismiss on unban confirmation dialog sends Reset event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), unbanUserAsyncAction = AsyncAction.ConfirmingNoParams, eventSink = eventsRecorder ), ) - rule.pressTag(TestTags.dialogNegative.value) + pressTag(TestTags.dialogNegative.value) eventsRecorder.assertSingle(InternalRoomMemberModerationEvents.Reset) } @Test - fun `disabled actions are not clickable`() { + fun `disabled actions are not clickable`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setRoomMemberModerationView( + setRoomMemberModerationView( aRoomMembersModerationState( selectedUser = anAlice(), actions = listOf( @@ -211,11 +210,11 @@ class RoomMemberModerationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_bottom_sheet_manage_room_member_remove) + clickOn(R.string.screen_bottom_sheet_manage_room_member_remove) } } -private fun AndroidComposeTestRule.setRoomMemberModerationView( +private fun AndroidComposeUiTest.setRoomMemberModerationView( state: InternalRoomMemberModerationState, onSelectAction: (ModerationAction, MatrixUser) -> Unit = EnsureNeverCalledWithTwoParams(), ) { diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt index d9324fdb91..f9729f74f0 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyViewTest.kt @@ -6,16 +6,19 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securebackup.impl.enter import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performImeAction import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.securebackup.impl.setup.views.aFormattedRecoveryKey import io.element.android.libraries.architecture.AsyncAction @@ -26,58 +29,54 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class SecureBackupEnterRecoveryKeyViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `back key pressed - calls onBackClick`() { + fun `back key pressed - calls onBackClick`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(), onBackClick = callback, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `back button clicked - calls onBackClick`() { + fun `back button clicked - calls onBackClick`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(), onBackClick = callback, ) - rule.pressBack() + pressBack() } } @Test @Config(qualifiers = "h1024dp") - fun `tapping on Continue when key is valid - calls expected action`() { + fun `tapping on Continue when key is valid - calls expected action`() = runAndroidComposeUiTest { val recorder = EventsRecorder() - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) } @Test - fun `entering a char emits the expected event`() { + fun `entering a char emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) - rule.onNodeWithText(keyValue).performTextInput("X") + onNodeWithText(keyValue).performTextInput("X") recorder.assertSingle( SecureBackupEnterRecoveryKeyEvents.OnRecoveryKeyChange("X$keyValue") ) @@ -85,43 +84,43 @@ class SecureBackupEnterRecoveryKeyViewTest { @Test @Config(qualifiers = "h1024dp") - fun `toggling the visibility of the textfield changes it`() { + fun `toggling the visibility of the textfield changes it`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() - rule.setSecureBackupEnterRecoveryKeyView(aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder)) + setSecureBackupEnterRecoveryKeyView(aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder)) // Initially, the text field should be visible - rule.onNodeWithText(keyValue).assertExists() + onNodeWithText(keyValue).assertExists() - rule.onNodeWithContentDescription(rule.activity.getString(CommonStrings.a11y_hide_password)).performClick() + onNodeWithContentDescription(activity!!.getString(CommonStrings.a11y_hide_password)).performClick() - rule.waitForIdle() + waitForIdle() recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.ChangeRecoveryKeyFieldContentsVisibility(false)) } @Test - fun `validating from keyboard emits the expected event`() { + fun `validating from keyboard emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val keyValue = aFormattedRecoveryKey() - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(isSubmitEnabled = true, eventSink = recorder), ) - rule.onNodeWithText(keyValue).performImeAction() + onNodeWithText(keyValue).performImeAction() recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) } @Test - fun `when submit action succeeds - calls onDone`() { + fun `when submit action succeeds - calls onDone`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setSecureBackupEnterRecoveryKeyView( + setSecureBackupEnterRecoveryKeyView( aSecureBackupEnterRecoveryKeyState(submitAction = AsyncAction.Success(Unit)), onDone = callback, ) } } - private fun AndroidComposeTestRule.setSecureBackupEnterRecoveryKeyView( + private fun AndroidComposeUiTest.setSecureBackupEnterRecoveryKeyView( state: SecureBackupEnterRecoveryKeyState, onDone: () -> Unit = EnsureNeverCalled(), onBackClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordViewTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordViewTest.kt index 6cfd061103..ce5972f66b 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordViewTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securebackup.impl.reset.password import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.ui.strings.CommonStrings @@ -22,64 +25,59 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 ResetIdentityPasswordViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `pressing the back HW button invokes the expected callback`() { + fun `pressing the back HW button invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setResetPasswordView( + setResetPasswordView( ResetIdentityPasswordState(resetAction = AsyncAction.Uninitialized, eventSink = {}), onBack = it, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `clicking on the back navigation button invokes the expected callback`() { + fun `clicking on the back navigation button invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setResetPasswordView( + setResetPasswordView( ResetIdentityPasswordState(resetAction = AsyncAction.Uninitialized, eventSink = {}), onBack = it, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking 'Reset identity' confirms the reset`() { + fun `clicking 'Reset identity' confirms the reset`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResetPasswordView( + setResetPasswordView( ResetIdentityPasswordState(resetAction = AsyncAction.Uninitialized, eventSink = eventsRecorder), ) - rule.onNodeWithText("Password").performTextInput("A password") + onNodeWithText("Password").performTextInput("A password") - rule.clickOn(CommonStrings.action_reset_identity) + clickOn(CommonStrings.action_reset_identity) eventsRecorder.assertSingle(ResetIdentityPasswordEvent.Reset("A password")) } @Test - fun `modifying the password dismisses the error state`() { + fun `modifying the password dismisses the error state`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResetPasswordView( + setResetPasswordView( ResetIdentityPasswordState(resetAction = AsyncAction.Failure(IllegalStateException("A failure")), eventSink = eventsRecorder), ) - rule.onNodeWithText("Password").performTextInput("A password") + onNodeWithText("Password").performTextInput("A password") eventsRecorder.assertSingle(ResetIdentityPasswordEvent.DismissError) } } -private fun AndroidComposeTestRule.setResetPasswordView( +private fun AndroidComposeUiTest.setResetPasswordView( state: ResetIdentityPasswordState, onBack: () -> Unit = EnsureNeverCalled(), ) { diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootViewTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootViewTest.kt index a913a9af27..0126d0d879 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootViewTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securebackup.impl.reset.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.securebackup.impl.R import io.element.android.libraries.ui.strings.CommonStrings @@ -20,76 +23,71 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack 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 import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class ResetIdentityRootViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `pressing the back HW button invokes the expected callback`() { + fun `pressing the back HW button invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setResetRootView( + setResetRootView( ResetIdentityRootState(displayConfirmationDialog = false, eventSink = {}), onBack = it, ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `clicking on the back navigation button invokes the expected callback`() { + fun `clicking on the back navigation button invokes the expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setResetRootView( + setResetRootView( ResetIdentityRootState(displayConfirmationDialog = false, eventSink = {}), onBack = it, ) - rule.pressBack() + pressBack() } } @Test @Config(qualifiers = "h720dp") - fun `clicking Continue displays the confirmation dialog`() { + fun `clicking Continue displays the confirmation dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResetRootView( + setResetRootView( ResetIdentityRootState(displayConfirmationDialog = false, eventSink = eventsRecorder), ) - rule.clickOn(R.string.screen_encryption_reset_action_continue_reset) + clickOn(R.string.screen_encryption_reset_action_continue_reset) eventsRecorder.assertSingle(ResetIdentityRootEvent.Continue) } @Test - fun `clicking 'Yes, reset now' confirms the reset`() { + fun `clicking 'Yes, reset now' confirms the reset`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setResetRootView( + setResetRootView( ResetIdentityRootState(displayConfirmationDialog = true, eventSink = {}), onContinue = it, ) - rule.clickOn(R.string.screen_reset_encryption_confirmation_alert_action) + clickOn(R.string.screen_reset_encryption_confirmation_alert_action) } } @Test - fun `clicking Cancel dismisses the dialog`() { + fun `clicking Cancel dismisses the dialog`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setResetRootView( + setResetRootView( ResetIdentityRootState(displayConfirmationDialog = true, eventSink = eventsRecorder), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ResetIdentityRootEvent.DismissDialog) } } -private fun AndroidComposeTestRule.setResetRootView( +private fun AndroidComposeUiTest.setResetRootView( state: ResetIdentityRootState, onBack: () -> Unit = EnsureNeverCalled(), onContinue: () -> Unit = EnsureNeverCalled(), diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt index 17d6f3a88d..2c0a6c9acb 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/editroomaddress/EditRoomAddressViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securityandprivacy.impl.editroomaddress import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity @@ -23,86 +26,82 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class EditRoomAddressViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `click on back invokes expected callback`() { + fun `click on back invokes expected callback`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setEditRoomAddressView(onBackClick = callback) - rule.pressBack() + setEditRoomAddressView(onBackClick = callback) + pressBack() } } @Test - fun `click on disabled save doesn't emit event`() { + fun `click on disabled save doesn't emit event`() = runAndroidComposeUiTest { val recorder = EventsRecorder(expectEvents = false) val state = anEditRoomAddressState(eventSink = recorder) - rule.setEditRoomAddressView(state) - rule.clickOn(CommonStrings.action_save) + setEditRoomAddressView(state) + clickOn(CommonStrings.action_save) recorder.assertEmpty() } @Test - fun `click on enabled save emits the expected event`() { + fun `click on enabled save emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "room", roomAddressValidity = RoomAddressValidity.Valid, eventSink = recorder ) - rule.setEditRoomAddressView(state) - rule.clickOn(CommonStrings.action_save) + setEditRoomAddressView(state) + clickOn(CommonStrings.action_save) recorder.assertSingle(EditRoomAddressEvents.Save) } @Test - fun `text changes on text field emits the expected event`() { + fun `text changes on text field emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", eventSink = recorder ) - rule.setEditRoomAddressView(state) + setEditRoomAddressView(state) - rule.onNodeWithTag(TestTags.roomAddressField.value).performTextInput("alias") + onNodeWithTag(TestTags.roomAddressField.value).performTextInput("alias") recorder.assertSingle(EditRoomAddressEvents.RoomAddressChanged("alias")) } @Test - fun `click on dismiss error emits the expected event`() { + fun `click on dismiss error emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", saveAction = AsyncAction.Failure(IllegalStateException()), eventSink = recorder ) - rule.setEditRoomAddressView(state) - rule.clickOn(CommonStrings.action_cancel) + setEditRoomAddressView(state) + clickOn(CommonStrings.action_cancel) recorder.assertSingle(EditRoomAddressEvents.DismissError) } @Test - fun `click on retry error emits the expected event`() { + fun `click on retry error emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = anEditRoomAddressState( roomAddress = "", saveAction = AsyncAction.Failure(IllegalStateException()), eventSink = recorder ) - rule.setEditRoomAddressView(state) - rule.clickOn(CommonStrings.action_retry) + setEditRoomAddressView(state) + clickOn(CommonStrings.action_retry) recorder.assertSingle(EditRoomAddressEvents.Save) } } -private fun AndroidComposeTestRule.setEditRoomAddressView( +private fun AndroidComposeUiTest.setEditRoomAddressView( state: EditRoomAddressState = anEditRoomAddressState( eventSink = EventsRecorder(expectEvents = false), ), diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesViewTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesViewTest.kt index c732df6df0..de6da41823 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesViewTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/manageauthorizedspaces/ManageAuthorizedSpacesViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securityandprivacy.impl.manageauthorizedspaces import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoom @@ -24,26 +27,22 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressBack import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ManageAuthorizedSpacesViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking back emits Cancel event`() { + fun `clicking back emits Cancel event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aManageAuthorizedSpacesState(eventSink = recorder) - rule.setManageAuthorizedSpacesView(state) - rule.pressBack() + setManageAuthorizedSpacesView(state) + pressBack() recorder.assertSingle(ManageAuthorizedSpacesEvent.Cancel) } @Test - fun `clicking space checkbox emits ToggleSpace event`() { + fun `clicking space checkbox emits ToggleSpace event`() = runAndroidComposeUiTest { val roomId = A_ROOM_ID val space = aSpaceRoom(roomId = roomId, displayName = "Test Space") val recorder = EventsRecorder() @@ -51,37 +50,37 @@ class ManageAuthorizedSpacesViewTest { selectableSpaces = listOf(space), eventSink = recorder ) - rule.setManageAuthorizedSpacesView(state) - rule.onNodeWithText("Test Space").performClick() + setManageAuthorizedSpacesView(state) + onNodeWithText("Test Space").performClick() recorder.assertSingle(ManageAuthorizedSpacesEvent.ToggleSpace(roomId)) } @Test - fun `clicking done button emits Done event`() { + fun `clicking done button emits Done event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aManageAuthorizedSpacesState( selectedIds = listOf(A_ROOM_ID), eventSink = recorder ) - rule.setManageAuthorizedSpacesView(state) - rule.clickOn(CommonStrings.action_done) + setManageAuthorizedSpacesView(state) + clickOn(CommonStrings.action_done) recorder.assertSingle(ManageAuthorizedSpacesEvent.Done) } @Test - fun `done button is disabled when no spaces selected`() { + fun `done button is disabled when no spaces selected`() = runAndroidComposeUiTest { val recorder = EventsRecorder(expectEvents = false) val state = aManageAuthorizedSpacesState( selectedIds = emptyList(), eventSink = recorder ) - rule.setManageAuthorizedSpacesView(state) - rule.clickOn(CommonStrings.action_done) + setManageAuthorizedSpacesView(state) + clickOn(CommonStrings.action_done) recorder.assertEmpty() } } -private fun AndroidComposeTestRule.setManageAuthorizedSpacesView( +private fun AndroidComposeUiTest.setManageAuthorizedSpacesView( state: ManageAuthorizedSpacesState = aManageAuthorizedSpacesState( eventSink = EventsRecorder(expectEvents = false) ), diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyViewTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyViewTest.kt index a1f46b2938..c46accbc91 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyViewTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyViewTest.kt @@ -5,13 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.securityandprivacy.impl.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.securityandprivacy.impl.R import io.element.android.libraries.architecture.AsyncAction @@ -23,73 +26,69 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.pressBack import kotlinx.collections.immutable.persistentListOf -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class SecurityAndPrivacyViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `click on back invokes emits the expected event`() { + fun `click on back invokes emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, ) - rule.setSecurityAndPrivacyView(state) - rule.pressBack() + setSecurityAndPrivacyView(state) + pressBack() recorder.assertSingle(SecurityAndPrivacyEvent.Exit) } @Test - fun `discard cancellation emits the expected event`() { + fun `discard cancellation emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = recorder, ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(CommonStrings.action_discard) + setSecurityAndPrivacyView(state) + clickOn(CommonStrings.action_discard) recorder.assertSingle(SecurityAndPrivacyEvent.Exit) } @Test - fun `save cancellation confirmation emits the expected event`() { + fun `save cancellation confirmation emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( saveAction = AsyncAction.ConfirmingCancellation, eventSink = recorder, ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(CommonStrings.action_save, inDialog = true) + setSecurityAndPrivacyView(state) + clickOn(CommonStrings.action_save, inDialog = true) recorder.assertSingle(SecurityAndPrivacyEvent.Save) } @Test - fun `click on room access item emits the expected event`() { + fun `click on room access item emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_room_access_invite_only_option_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_room_access_invite_only_option_title) recorder.assertSingle(SecurityAndPrivacyEvent.ChangeRoomAccess(SecurityAndPrivacyRoomAccess.InviteOnly)) } @Test - fun `click on disabled save doesn't emit event`() { + fun `click on disabled save doesn't emit event`() = runAndroidComposeUiTest { val recorder = EventsRecorder(expectEvents = false) val state = aSecurityAndPrivacyState(eventSink = recorder) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(CommonStrings.action_save) + setSecurityAndPrivacyView(state) + clickOn(CommonStrings.action_save) recorder.assertEmpty() } @Test - fun `click on enabled save emits the expected event`() { + fun `click on enabled save emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, @@ -97,14 +96,14 @@ class SecurityAndPrivacyViewTest { roomAccess = SecurityAndPrivacyRoomAccess.Anyone, ) ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(CommonStrings.action_save) + setSecurityAndPrivacyView(state) + clickOn(CommonStrings.action_save) recorder.assertSingle(SecurityAndPrivacyEvent.Save) } @Test @Config(qualifiers = "h640dp") - fun `click on room address item emits the expected event`() { + fun `click on room address item emits the expected event`() = runAndroidComposeUiTest { val address = "@alias:matrix.org" val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( @@ -114,14 +113,14 @@ class SecurityAndPrivacyViewTest { roomAccess = SecurityAndPrivacyRoomAccess.Anyone, ), ) - rule.setSecurityAndPrivacyView(state) - rule.onNodeWithText(address).performClick() + setSecurityAndPrivacyView(state) + onNodeWithText(address).performClick() recorder.assertSingle(SecurityAndPrivacyEvent.EditRoomAddress) } @Test @Config(qualifiers = "h1024dp") - fun `click on room visibility item emits the expected event`() { + fun `click on room visibility item emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, @@ -130,14 +129,14 @@ class SecurityAndPrivacyViewTest { isVisibleInRoomDirectory = AsyncData.Success(false), ), ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_room_directory_visibility_toggle_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_room_directory_visibility_toggle_title) recorder.assertSingle(SecurityAndPrivacyEvent.ToggleRoomVisibility) } @Test @Config(qualifiers = "h1024dp") - fun `click on history visibility item emits the expected event`() { + fun `click on history visibility item emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, @@ -145,65 +144,65 @@ class SecurityAndPrivacyViewTest { historyVisibility = SecurityAndPrivacyHistoryVisibility.Invited, ), ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_room_history_since_invite_option_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_room_history_since_invite_option_title) recorder.assertSingle(SecurityAndPrivacyEvent.ChangeHistoryVisibility(SecurityAndPrivacyHistoryVisibility.Invited)) } @Test @Config(qualifiers = "h1024dp") - fun `click on encryption item emits the expected event`() { + fun `click on encryption item emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, savedSettings = aSecurityAndPrivacySettings(isEncrypted = false), ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_encryption_toggle_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_encryption_toggle_title) recorder.assertSingle(SecurityAndPrivacyEvent.ToggleEncryptionState) } @Test - fun `click on encryption confirm emits the expected event`() { + fun `click on encryption confirm emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, showEncryptionConfirmation = true, ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_enable_encryption_alert_confirm_button_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_enable_encryption_alert_confirm_button_title) recorder.assertSingle(SecurityAndPrivacyEvent.ConfirmEnableEncryption) } @Test @Config(qualifiers = "h1024dp") - fun `click on space member access emits the expected event`() { + fun `click on space member access emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, spaceSelectionMode = SpaceSelectionMode.Single(A_ROOM_ID, null), ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_room_access_space_members_option_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_room_access_space_members_option_title) recorder.assertSingle(SecurityAndPrivacyEvent.SelectSpaceMemberAccess) } @Test @Config(qualifiers = "h1024dp") - fun `click on ask to join with space members emits the expected event`() { + fun `click on ask to join with space members emits the expected event`() = runAndroidComposeUiTest { val recorder = EventsRecorder() val state = aSecurityAndPrivacyState( eventSink = recorder, spaceSelectionMode = SpaceSelectionMode.Single(A_ROOM_ID, null), ) - rule.setSecurityAndPrivacyView(state) - rule.clickOn(R.string.screen_security_and_privacy_ask_to_join_option_title) + setSecurityAndPrivacyView(state) + clickOn(R.string.screen_security_and_privacy_ask_to_join_option_title) recorder.assertSingle(SecurityAndPrivacyEvent.SelectAskToJoinWithSpaceMembersAccess) } @Test @Config(qualifiers = "h1024dp") - fun `manage spaces footer is shown when space member access is selected`() { + fun `manage spaces footer is shown when space member access is selected`() = runAndroidComposeUiTest { val recorder = EventsRecorder(expectEvents = false) val state = aSecurityAndPrivacyState( eventSink = recorder, @@ -212,15 +211,16 @@ class SecurityAndPrivacyViewTest { roomAccess = SecurityAndPrivacyRoomAccess.SpaceMember(persistentListOf(A_ROOM_ID)), ), ) - rule.setSecurityAndPrivacyView(state) + setSecurityAndPrivacyView(state) // The footer text uses AnnotatedString with a link. Verify the footer text is displayed. - val actionFooterText = rule.activity.getString(R.string.screen_security_and_privacy_room_access_footer_manage_spaces_action) - val footerText = rule.activity.getString(R.string.screen_security_and_privacy_room_access_footer, actionFooterText) - rule.onNodeWithText(footerText).assertExists() + val resources = activity!!.resources + val actionFooterText = resources.getString(R.string.screen_security_and_privacy_room_access_footer_manage_spaces_action) + val footerText = resources.getString(R.string.screen_security_and_privacy_room_access_footer, actionFooterText) + onNodeWithText(footerText).assertExists() } } -private fun AndroidComposeTestRule.setSecurityAndPrivacyView( +private fun AndroidComposeUiTest.setSecurityAndPrivacyView( state: SecurityAndPrivacyState = aSecurityAndPrivacyState( eventSink = EventsRecorder(expectEvents = false), ), diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt index d75fecd05a..6fc10f1e82 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt @@ -5,13 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.space.impl.addroom import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.theme.components.SearchBarResultState @@ -22,77 +25,73 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import kotlinx.collections.immutable.toImmutableList -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class AddRoomToSpaceViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking back when search inactive emits Dismiss and invokes onBackClick`() { + fun `clicking back when search inactive emits Dismiss and invokes onBackClick`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() ensureCalledOnce { - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( isSearchActive = false, eventSink = eventsRecorder, ), onBackClick = it, ) - rule.pressBack() + pressBack() } eventsRecorder.assertSingle(AddRoomToSpaceEvent.Dismiss) } @Test - fun `clicking back when search active emits CloseSearch event`() { + fun `clicking back when search active emits CloseSearch event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( isSearchActive = true, eventSink = eventsRecorder, ), ) - rule.pressBack() + pressBack() eventsRecorder.assertSingle(AddRoomToSpaceEvent.OnSearchActiveChanged(false)) } @Test - fun `clicking save emits Save event`() { + fun `clicking save emits Save event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( selectedRooms = aSelectRoomInfoList().take(1).toImmutableList(), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_save) + clickOn(CommonStrings.action_save) eventsRecorder.assertSingle(AddRoomToSpaceEvent.Save) } @Config(qualifiers = "h1024dp") @Test - fun `clicking room in suggestions emits ToggleRoom event`() { + fun `clicking room in suggestions emits ToggleRoom event`() = runAndroidComposeUiTest { val suggestions = aSelectRoomInfoList() val eventsRecorder = EventsRecorder() - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( suggestions = suggestions, eventSink = eventsRecorder, ), ) - rule.onNodeWithText(suggestions.first().name!!).performClick() + onNodeWithText(suggestions.first().name!!).performClick() eventsRecorder.assertSingle(AddRoomToSpaceEvent.ToggleRoom(suggestions.first())) } @Test - fun `onRoomsAdded called when saveAction is Success`() { + fun `onRoomsAdded called when saveAction is Success`() = runAndroidComposeUiTest { ensureCalledOnce { - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( saveAction = AsyncAction.Success(Unit), ), @@ -103,10 +102,10 @@ class AddRoomToSpaceViewTest { @Config(qualifiers = "h1024dp") @Test - fun `displaying search results sends UpdateSearchVisibleRange event`() { + fun `displaying search results sends UpdateSearchVisibleRange event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val rooms = aSelectRoomInfoList() - rule.setAddRoomToSpaceView( + setAddRoomToSpaceView( anAddRoomToSpaceState( isSearchActive = true, searchResults = SearchBarResultState.Results(rooms), @@ -117,7 +116,7 @@ class AddRoomToSpaceViewTest { } } -private fun AndroidComposeTestRule.setAddRoomToSpaceView( +private fun AndroidComposeUiTest.setAddRoomToSpaceView( state: AddRoomToSpaceState, onBackClick: () -> Unit = EnsureNeverCalled(), onRoomsAdded: () -> Unit = EnsureNeverCalled(), diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt index 87343b6e34..6632c7f4f8 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpaceViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.space.impl.root import androidx.activity.ComponentActivity import androidx.compose.runtime.Composable -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.room.CurrentUserMembership @@ -33,37 +36,33 @@ import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.pressBack 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 import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class SpaceViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setSpaceView( + setSpaceView( aSpaceState( hasMoreToLoad = false, eventSink = eventsRecorder, ), onBackClick = it, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on a room name invokes the expected callback`() { + fun `clicking on a room name invokes the expected callback`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME) val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(aSpaceRoom) { - rule.setSpaceView( + setSpaceView( aSpaceState( children = listOf(aSpaceRoom), hasMoreToLoad = false, @@ -71,91 +70,91 @@ class SpaceViewTest { ), onRoomClick = it, ) - rule.onNodeWithText(A_ROOM_NAME).performClick() + onNodeWithText(A_ROOM_NAME).performClick() } } @Test - fun `clicking on Join room emits the expected Event`() { + fun `clicking on Join room emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = null) val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( children = listOf(aSpaceRoom), hasMoreToLoad = false, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_join) + clickOn(CommonStrings.action_join) eventsRecorder.assertSingle(SpaceEvents.Join(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on accept invite emits the expected Event`() { + fun `clicking on accept invite emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( hasMoreToLoad = false, children = listOf(aSpaceRoom), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_accept) + clickOn(CommonStrings.action_accept) eventsRecorder.assertSingle(SpaceEvents.AcceptInvite(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on decline invite emits the expected Event`() { + fun `clicking on decline invite emits the expected Event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, state = CurrentUserMembership.INVITED) val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( hasMoreToLoad = false, children = listOf(aSpaceRoom), eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_decline) + clickOn(CommonStrings.action_decline) eventsRecorder.assertSingle(SpaceEvents.DeclineInvite(aSpaceRoom)) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on topic emits the expected Event`() { + fun `clicking on topic emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( spaceInfo = aRoomInfo(topic = A_ROOM_TOPIC), hasMoreToLoad = false, eventSink = eventsRecorder, ) ) - rule.onNodeWithText(A_ROOM_TOPIC).performClick() + onNodeWithText(A_ROOM_TOPIC).performClick() eventsRecorder.assertSingle(SpaceEvents.ShowTopicViewer(A_ROOM_TOPIC)) } @Test - fun `clicking back in manage mode emits ExitManageMode event`() { + fun `clicking back in manage mode emits ExitManageMode event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( hasMoreToLoad = false, isManageMode = true, eventSink = eventsRecorder, ) ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(SpaceEvents.ExitManageMode) } @Test - fun `clicking on room in manage mode emits ToggleRoomSelection event`() { + fun `clicking on room in manage mode emits ToggleRoomSelection event`() = runAndroidComposeUiTest { val aSpaceRoom = aSpaceRoom(roomId = A_ROOM_ID, displayName = A_ROOM_NAME) val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( children = listOf(aSpaceRoom), hasMoreToLoad = false, @@ -163,14 +162,14 @@ class SpaceViewTest { eventSink = eventsRecorder, ) ) - rule.onNodeWithText(A_ROOM_NAME).performClick() + onNodeWithText(A_ROOM_NAME).performClick() eventsRecorder.assertSingle(SpaceEvents.ToggleRoomSelection(A_ROOM_ID)) } @Test - fun `clicking remove button emits RemoveSelectedRooms event`() { + fun `clicking remove button emits RemoveSelectedRooms event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( children = listOf(aSpaceRoom(roomId = A_ROOM_ID)), hasMoreToLoad = false, @@ -179,15 +178,15 @@ class SpaceViewTest { eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_remove) + clickOn(CommonStrings.action_remove) eventsRecorder.assertSingle(SpaceEvents.RemoveSelectedRooms) } @Config(qualifiers = "h1024dp") @Test - fun `clicking confirm in removal dialog emits ConfirmRoomRemoval event`() { + fun `clicking confirm in removal dialog emits ConfirmRoomRemoval event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setSpaceView( + setSpaceView( aSpaceState( children = listOf(aSpaceRoom(roomId = A_ROOM_ID)), hasMoreToLoad = false, @@ -198,14 +197,14 @@ class SpaceViewTest { ) ) // Click on the Remove button in the confirmation dialog - rule.clickOn(CommonStrings.action_remove, inDialog = true) + clickOn(CommonStrings.action_remove, inDialog = true) eventsRecorder.assertSingle(SpaceEvents.ConfirmRoomRemoval) } @Test - fun `clicking create room button calls the expected callback`() { + fun `clicking create room button calls the expected callback`() = runAndroidComposeUiTest { val onCreateRoomClick = lambdaRecorder { } - rule.setSpaceView( + setSpaceView( aSpaceState( children = emptyList(), hasMoreToLoad = false, @@ -214,14 +213,14 @@ class SpaceViewTest { ), onCreateRoomClick = onCreateRoomClick, ) - rule.clickOn(CommonStrings.action_create_room) + clickOn(CommonStrings.action_create_room) onCreateRoomClick.assertions().isCalledOnce() } @Test - fun `clicking add existing room button calls the expected callback`() { + fun `clicking add existing room button calls the expected callback`() = runAndroidComposeUiTest { val onAddRoomClick = lambdaRecorder { } - rule.setSpaceView( + setSpaceView( aSpaceState( children = emptyList(), hasMoreToLoad = false, @@ -230,12 +229,12 @@ class SpaceViewTest { ), onAddRoomClick = onAddRoomClick, ) - rule.clickOn(CommonStrings.action_add_existing_rooms) + clickOn(CommonStrings.action_add_existing_rooms) onAddRoomClick.assertions().isCalledOnce() } } -private fun AndroidComposeTestRule.setSpaceView( +private fun AndroidComposeUiTest.setSpaceView( state: SpaceState, onBackClick: () -> Unit = EnsureNeverCalled(), onRoomClick: (SpaceRoom) -> Unit = EnsureNeverCalledWithParam(), diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressViewTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressViewTest.kt index 92162ca82c..dd992a9d2f 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressViewTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressViewTest.kt @@ -6,56 +6,54 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.startchat.impl.joinbyaddress import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.startchat.impl.R 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.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class JoinBaseRoomByAddressViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `entering text emits the expected event`() { + fun `entering text emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomByAddressView( + setJoinRoomByAddressView( aJoinRoomByAddressState( eventSink = eventsRecorder, ) ) - val text = rule.activity.getString(R.string.screen_start_chat_join_room_by_address_action) - rule.onNodeWithText(text).performTextInput("#address:matrix.org") + val text = activity!!.getString(R.string.screen_start_chat_join_room_by_address_action) + onNodeWithText(text).performTextInput("#address:matrix.org") eventsRecorder.assertSingle(JoinRoomByAddressEvent.UpdateAddress("#address:matrix.org")) } @Test - fun `clicking on continue emits the expected event`() { + fun `clicking on continue emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setJoinRoomByAddressView( + setJoinRoomByAddressView( aJoinRoomByAddressState( eventSink = eventsRecorder, ) ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) eventsRecorder.assertSingle(JoinRoomByAddressEvent.Continue) } } -private fun AndroidComposeTestRule.setJoinRoomByAddressView( +private fun AndroidComposeUiTest.setJoinRoomByAddressView( state: JoinRoomByAddressState, ) { setSafeContent { diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt index 9237f3433c..abcb70113b 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.startchat.impl.root import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.startchat.impl.R import io.element.android.features.startchat.impl.userlist.aRecentDirectRoomList @@ -27,70 +30,65 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class StartChatViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on back invokes the expected callback`() { + fun `clicking on back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( eventSink = eventsRecorder, ), onCloseClick = it ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on New room invokes the expected callback`() { + fun `clicking on New room invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( eventSink = eventsRecorder, ), onNewRoomClick = it ) - rule.clickOn(R.string.screen_create_room_action_create_room) + clickOn(R.string.screen_create_room_action_create_room) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on Invite people invokes the expected callback`() { + fun `clicking on Invite people invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( applicationName = "test", eventSink = eventsRecorder, ), onInviteFriendsClick = it ) - val text = rule.activity.getString(CommonStrings.action_invite_friends_to_app, "test") - rule.onNodeWithText(text).performClick() + val text = activity!!.getString(CommonStrings.action_invite_friends_to_app, "test") + onNodeWithText(text).performClick() } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on a user suggestion invokes the expected callback`() { + fun `clicking on a user suggestion invokes the expected callback`() = runAndroidComposeUiTest { val recentDirectRoomList = aRecentDirectRoomList() val firstRoom = recentDirectRoomList[0] val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnceWithParam(firstRoom.roomId) { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( userListState = aUserListState( recentDirectRooms = recentDirectRoomList @@ -99,42 +97,42 @@ class StartChatViewTest { ), onOpenDM = it ) - rule.onNodeWithText(firstRoom.matrixUser.getBestName()).performClick() + onNodeWithText(firstRoom.matrixUser.getBestName()).performClick() } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on Join room by address invokes the expected callback`() { + fun `clicking on Join room by address invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( eventSink = eventsRecorder, ), onJoinRoomByAddressClick = it ) - rule.clickOn(R.string.screen_start_chat_join_room_by_address_action) + clickOn(R.string.screen_start_chat_join_room_by_address_action) } } @Test - fun `clicking on room directory invokes the expected callback`() { + fun `clicking on room directory invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setStartChatView( + setStartChatView( aCreateRoomRootState( eventSink = eventsRecorder, isRoomDirectorySearchEnabled = true ), onRoomDirectorySearchClick = it ) - rule.clickOn(R.string.screen_room_directory_search_title) + clickOn(R.string.screen_room_directory_search_title) } } } -private fun AndroidComposeTestRule.setStartChatView( +private fun AndroidComposeUiTest.setStartChatView( state: StartChatState, onCloseClick: () -> Unit = EnsureNeverCalled(), onNewRoomClick: () -> Unit = EnsureNeverCalled(), diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt index 83b10e2a53..b1d81f374c 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt @@ -6,13 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.userprofile import androidx.activity.ComponentActivity +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.hasTestTag -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState @@ -39,193 +42,188 @@ import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.ensureCalledOnceWithTwoParams import io.element.android.tests.testutils.pressBack -import kotlinx.coroutines.test.runTest -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class UserProfileViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `on back button click - the expected callback is called`() = runTest { + fun `on back button click - the expected callback is called`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setUserProfileView( + setUserProfileView( goBack = callback, ) - rule.pressBack() + pressBack() } } @Test - fun `on avatar clicked - the expected callback is called`() = runTest { + fun `on avatar clicked - the expected callback is called`() = runAndroidComposeUiTest { ensureCalledOnceWithTwoParams(A_USER_NAME, AN_AVATAR_URL) { callback -> - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState(userName = A_USER_NAME, avatarUrl = AN_AVATAR_URL), openAvatarPreview = callback, ) - rule.onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() + onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() } } @Test - fun `on avatar clicked with no avatar - nothing happens`() = runTest { + fun `on avatar clicked with no avatar - nothing happens`() = runAndroidComposeUiTest { val callback = EnsureNeverCalledWithTwoParams() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState(userName = A_USER_NAME, avatarUrl = null), openAvatarPreview = callback, ) - rule.onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() + onNode(hasTestTag(TestTags.memberDetailAvatar.value)).performClick() } @Test - fun `on Share clicked - the expected callback is called`() = runTest { + fun `on Share clicked - the expected callback is called`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setUserProfileView( + setUserProfileView( onShareUser = callback, ) - rule.clickOn(CommonStrings.action_share) + clickOn(CommonStrings.action_share) } } @Test - fun `on Message clicked - the StartDm event is emitted`() = runTest { + fun `on Message clicked - the StartDm event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( dmRoomId = A_ROOM_ID, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_message) + clickOn(CommonStrings.action_message) eventsRecorder.assertSingle(UserProfileEvents.StartDM) } @Test - fun `on Call clicked - the expected callback is called`() = runTest { + fun `on Call clicked - the expected callback is called`() = runAndroidComposeUiTest { ensureCalledOnceWithTwoParams(A_ROOM_ID, CallIntent.AUDIO) { callback -> - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( dmRoomId = A_ROOM_ID, canCall = true, ), onStartCall = callback, ) - rule.clickOn(CommonStrings.action_call) + clickOn(CommonStrings.action_call) } } @Test - fun `on Video Call clicked - the expected callback is called`() = runTest { + fun `on Video Call clicked - the expected callback is called`() = runAndroidComposeUiTest { ensureCalledOnceWithTwoParams(A_ROOM_ID, CallIntent.VIDEO) { callback -> - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( dmRoomId = A_ROOM_ID, canCall = true, ), onStartCall = callback, ) - rule.clickOn(CommonStrings.common_video) + clickOn(CommonStrings.common_video) } } @Config(qualifiers = "h1024dp") @Test - fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runTest { + fun `on Block user clicked - a BlockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_dm_details_block_user) + clickOn(R.string.screen_dm_details_block_user) eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = true)) } @Test - fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runTest { + fun `on confirming block user - a BlockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_dm_details_block_alert_action) + clickOn(R.string.screen_dm_details_block_alert_action) eventsRecorder.assertSingle(UserProfileEvents.BlockUser(needsConfirmation = false)) } @Test - fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runTest { + fun `on canceling blocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) } @Config(qualifiers = "h1024dp") @Test - fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runTest { + fun `on Unblock user clicked - an UnblockUser event is emitted with needsConfirmation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_dm_details_unblock_user) + clickOn(R.string.screen_dm_details_unblock_user) eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = true)) } @Test - fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runTest { + fun `on confirming Unblock user - an UnblockUser event is emitted without needsConfirmation`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, eventSink = eventsRecorder, ), ) - rule.clickOn(R.string.screen_dm_details_unblock_alert_action) + clickOn(R.string.screen_dm_details_unblock_alert_action) eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(needsConfirmation = false)) } @Test - fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runTest { + fun `on canceling unblocking a user - a ClearConfirmationDialog event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState( isBlocked = AsyncData.Success(true), displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, eventSink = eventsRecorder, ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) } @Test - fun `on verify user clicked - the right callback is called`() = runTest { + fun `on verify user clicked - the right callback is called`() = runAndroidComposeUiTest { ensureCalledOnceWithParam(A_USER_ID) { callback -> - rule.setUserProfileView( + setUserProfileView( state = aUserProfileState(userId = A_USER_ID, verificationState = UserProfileVerificationState.UNVERIFIED), onVerifyClick = callback, ) - rule.clickOn(CommonStrings.common_verify_user) + clickOn(CommonStrings.common_verify_user) } } } -private fun AndroidComposeTestRule.setUserProfileView( +private fun AndroidComposeUiTest.setUserProfileView( state: UserProfileState = aUserProfileState( eventSink = EventsRecorder(expectEvents = false), ), diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt index 3219658796..3498ad7714 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserDialogsTest.kt @@ -6,10 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.features.userprofile.shared.blockuser -import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState @@ -18,18 +20,15 @@ import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class BlockUserDialogsTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `confirm block user emit expected Event`() { + fun `confirm block user emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { BlockUserDialogs( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, @@ -37,14 +36,14 @@ class BlockUserDialogsTest { ) ) } - rule.clickOn(R.string.screen_dm_details_block_alert_action) + clickOn(R.string.screen_dm_details_block_alert_action) eventsRecorder.assertSingle(UserProfileEvents.BlockUser(false)) } @Test - fun `cancel block user emit expected Event`() { + fun `cancel block user emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { BlockUserDialogs( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block, @@ -52,14 +51,14 @@ class BlockUserDialogsTest { ) ) } - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) } @Test - fun `confirm unblock user emit expected Event`() { + fun `confirm unblock user emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { BlockUserDialogs( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, @@ -67,14 +66,14 @@ class BlockUserDialogsTest { ) ) } - rule.clickOn(R.string.screen_dm_details_unblock_alert_action) + clickOn(R.string.screen_dm_details_unblock_alert_action) eventsRecorder.assertSingle(UserProfileEvents.UnblockUser(false)) } @Test - fun `cancel unblock user emit expected Event`() { + fun `cancel unblock user emit expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setContent { + setContent { BlockUserDialogs( state = aUserProfileState( displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock, @@ -82,7 +81,7 @@ class BlockUserDialogsTest { ) ) } - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) } } 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 index 4aa63f3ab8..afab77ad76 100644 --- 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 @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + 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.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.verifysession.impl.R import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData @@ -18,59 +21,55 @@ 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`() { + fun `back key pressed - ignore the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @Test - fun `ignore incoming verification emits the expected event`() { + fun `ignore incoming verification emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_ignore) + clickOn(CommonStrings.action_ignore) eventsRecorder.assertSingle(IncomingVerificationViewEvents.IgnoreVerification) } @Test - fun `start incoming verification emits the expected event`() { + fun `start incoming verification emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial(), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_start_verification) + clickOn(CommonStrings.action_start_verification) eventsRecorder.assertSingle(IncomingVerificationViewEvents.StartVerification) } @Test - fun `back key pressed - when awaiting response cancels the verification`() { + fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = aStepInitial( isWaiting = true, @@ -78,16 +77,16 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } // endregion step Initial // region step Verifying @Test - fun `back key pressed - when ready to verify cancels the verification`() { + fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -96,14 +95,14 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @Test - fun `back key pressed - when verifying and loading emits the expected event`() { + fun `back key pressed - when verifying and loading emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -112,14 +111,14 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @Test - fun `clicking on they do not match emits the expected event`() { + fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -128,14 +127,14 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_session_verification_they_dont_match) + clickOn(R.string.screen_session_verification_they_dont_match) eventsRecorder.assertSingle(IncomingVerificationViewEvents.DeclineVerification) } @Test - fun `clicking on they match emits the expected event`() { + fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -144,35 +143,35 @@ class IncomingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_session_verification_they_match) + 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`() { + fun `back key pressed - when failure resets the flow`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Failure, eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @Test - fun `click on done - when failure resets the flow`() { + fun `click on done - when failure resets the flow`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Failure, eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_done) + clickOn(CommonStrings.action_done) eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @@ -180,33 +179,33 @@ class IncomingVerificationViewTest { // region step Completed @Test - fun `back key pressed - on Completed step emits the expected event`() { + fun `back key pressed - on Completed step emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Completed, eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } @Test - fun `when flow is completed and the user clicks on the done button, the expected event is emitted`() { + fun `when flow is completed and the user clicks on the done button, the expected event is emitted`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setIncomingVerificationView( + setIncomingVerificationView( anIncomingVerificationState( step = IncomingVerificationState.Step.Completed, eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_done) + clickOn(CommonStrings.action_done) eventsRecorder.assertSingle(IncomingVerificationViewEvents.GoBack) } // endregion - private fun AndroidComposeTestRule.setIncomingVerificationView( + private fun AndroidComposeUiTest.setIncomingVerificationView( state: IncomingVerificationState, ) { setContent { diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt index 71b55fac10..1c96c5c2af 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationViewTest.kt @@ -6,11 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + 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.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.verifysession.impl.R import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData @@ -21,58 +24,54 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBackKey -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class OutgoingVerificationViewTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `back key pressed - when canceled resets the flow`() { + fun `back key pressed - when canceled resets the flow`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Canceled, eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Reset) } @Test - fun `back key pressed - when awaiting response cancels the verification`() { + fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.AwaitingOtherDeviceResponse, eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) } @Test - fun `back key pressed - when ready to verify cancels the verification`() { + fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Ready, eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) } @Test - fun `back key pressed - when verifying and not loading declines the verification`() { + fun `back key pressed - when verifying and not loading declines the verification`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -81,14 +80,14 @@ class OutgoingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) } @Test - fun `back key pressed - when verifying and loading does nothing`() { + fun `back key pressed - when verifying and loading does nothing`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -97,42 +96,42 @@ class OutgoingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.pressBackKey() + pressBackKey() eventsRecorder.assertEmpty() } @Test - fun `back key pressed - on Completed exits the flow`() { + fun `back key pressed - on Completed exits the flow`() = runAndroidComposeUiTest { ensureCalledOnce { callback -> - rule.setOutgoingVerificationView( + setOutgoingVerificationView( onBack = callback, state = anOutgoingVerificationState( step = OutgoingVerificationState.Step.Completed, ), ) - rule.pressBackKey() + pressBackKey() } } @Test - fun `when flow is completed and the user clicks on the continue button, the expected callback is invoked`() { + fun `when flow is completed and the user clicks on the continue button, the expected callback is invoked`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { callback -> - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Completed, eventSink = eventsRecorder ), onFinished = callback, ) - rule.clickOn(CommonStrings.action_continue) + clickOn(CommonStrings.action_continue) } } @Test - fun `clicking on they match emits the expected event`() { + fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -141,14 +140,14 @@ class OutgoingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_session_verification_they_match) + clickOn(R.string.screen_session_verification_they_match) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.ConfirmVerification) } @Test - fun `clicking on they do not match emits the expected event`() { + fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setOutgoingVerificationView( + setOutgoingVerificationView( anOutgoingVerificationState( step = OutgoingVerificationState.Step.Verifying( data = aEmojisSessionVerificationData(), @@ -157,11 +156,11 @@ class OutgoingVerificationViewTest { eventSink = eventsRecorder ), ) - rule.clickOn(R.string.screen_session_verification_they_dont_match) + clickOn(R.string.screen_session_verification_they_dont_match) eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) } - private fun AndroidComposeTestRule.setOutgoingVerificationView( + private fun AndroidComposeUiTest.setOutgoingVerificationView( state: OutgoingVerificationState, onLearnMoreClick: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(), diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheetTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheetTest.kt index 4cbb35a85e..3f4c3aa6c3 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheetTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheetTest.kt @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.mediaviewer.impl.details import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.ui.strings.CommonStrings @@ -21,43 +24,38 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class MediaDeleteConfirmationBottomSheetTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on Cancel invokes expected callback`() { + fun `clicking on Cancel invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDeleteConfirmation() ensureCalledOnce { callback -> - rule.setMediaDeleteConfirmationBottomSheet( + setMediaDeleteConfirmationBottomSheet( state = state, onDismiss = callback, ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) } } @Test - fun `clicking on Remove invokes expected callback`() { + fun `clicking on Remove invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDeleteConfirmation() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDeleteConfirmationBottomSheet( + setMediaDeleteConfirmationBottomSheet( state = state, onDelete = callback, ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_remove)).assertExists() - rule.clickOn(CommonStrings.action_remove) + onNodeWithText(activity!!.getString(CommonStrings.action_remove)).assertExists() + clickOn(CommonStrings.action_remove) } } } -private fun AndroidComposeTestRule.setMediaDeleteConfirmationBottomSheet( +private fun AndroidComposeUiTest.setMediaDeleteConfirmationBottomSheet( state: MediaBottomSheetState.DeleteConfirmation, onDelete: (EventId) -> Unit = EnsureNeverCalledWithParam(), onDismiss: () -> Unit = EnsureNeverCalled(), diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt index 21a06f9568..5b0f105aea 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.mediaviewer.impl.details import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.ui.strings.CommonStrings @@ -20,97 +23,92 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.setSafeContent -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class MediaDetailsBottomSheetTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test @Config(qualifiers = "h1024dp") - fun `clicking on View in timeline invokes expected callback`() { + fun `clicking on View in timeline invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, onViewInTimeline = callback, ) - rule.clickOn(CommonStrings.action_view_in_timeline) + clickOn(CommonStrings.action_view_in_timeline) } } @Test @Config(qualifiers = "h1024dp") - fun `clicking on Share invokes expected callback`() { + fun `clicking on Share invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, onShare = callback, ) - rule.clickOn(CommonStrings.action_share) + clickOn(CommonStrings.action_share) } } @Test @Config(qualifiers = "h1024dp") - fun `clicking on Forward invokes expected callback`() { + fun `clicking on Forward invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, onForward = callback, ) - rule.clickOn(CommonStrings.action_forward) + clickOn(CommonStrings.action_forward) } } @Test @Config(qualifiers = "h1024dp") - fun `clicking on Download invokes expected callback`() { + fun `clicking on Download invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, onDownload = callback, ) - rule.clickOn(CommonStrings.action_download) + clickOn(CommonStrings.action_download) } } @Config(qualifiers = "h1024dp") @Test - fun `clicking on Delete invokes expected callback`() { + fun `clicking on Delete invokes expected callback`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails() ensureCalledOnceWithParam(state.eventId) { callback -> - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, onDelete = callback, ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_delete)).assertExists() - rule.clickOn(CommonStrings.action_delete) + onNodeWithText(activity!!.getString(CommonStrings.action_delete)).assertExists() + clickOn(CommonStrings.action_delete) } } @Config(qualifiers = "h1024dp") @Test - fun `Remove is not present if canDelete is false`() { + fun `Remove is not present if canDelete is false`() = runAndroidComposeUiTest { val state = aMediaBottomSheetStateDetails( canDelete = false, ) - rule.setMediaDetailsBottomSheet( + setMediaDetailsBottomSheet( state = state, ) - rule.onNodeWithText(rule.activity.getString(CommonStrings.action_remove)).assertDoesNotExist() + onNodeWithText(activity!!.getString(CommonStrings.action_remove)).assertDoesNotExist() } } -private fun AndroidComposeTestRule.setMediaDetailsBottomSheet( +private fun AndroidComposeUiTest.setMediaDetailsBottomSheet( state: MediaBottomSheetState.Details, onViewInTimeline: (EventId) -> Unit = EnsureNeverCalledWithParam(), onShare: (EventId) -> Unit = EnsureNeverCalledWithParam(), diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index 9eded788aa..fdd447c4a6 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -6,18 +6,21 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.mediaviewer.impl.viewer import android.net.Uri import androidx.activity.ComponentActivity import androidx.annotation.StringRes +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertHasClickAction -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeDown +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.mediaviewer.impl.details.aMediaBottomSheetStateDetails @@ -30,30 +33,26 @@ import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.setSafeContent import io.mockk.mockk -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class MediaViewerViewTest { - @get:Rule val rule = createAndroidComposeRule() - private val mockMediaUrl: Uri = mockk("localMediaUri") @Test - fun `clicking on back invokes expected callback`() { + fun `clicking on back invokes expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMediaViewerState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMediaViewerView( + setMediaViewerView( state = state, onBackClick = callback, ) - rule.pressBack() + pressBack() } eventsRecorder.assertList( listOf( @@ -103,16 +102,16 @@ class MediaViewerViewTest { data: MediaViewerPageData.MediaViewerData, @StringRes contentDescriptionRes: Int, expectedEvent: MediaViewerEvent, - ) { + ) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setMediaViewerView( + setMediaViewerView( aMediaViewerState( listData = listOf(data), eventSink = eventsRecorder ), ) - val contentDescription = rule.activity.getString(contentDescriptionRes) - rule.onNodeWithContentDescription(contentDescription).performClick() + val contentDescription = activity!!.getString(contentDescriptionRes) + onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertList( listOf( MediaViewerEvent.OnNavigateTo(0), @@ -159,16 +158,16 @@ class MediaViewerViewTest { data: MediaViewerPageData.MediaViewerData, @StringRes textRes: Int, expectedEvent: MediaViewerEvent, - ) { + ) = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setMediaViewerView( + setMediaViewerView( aMediaViewerState( listData = listOf(data), mediaBottomSheetState = aMediaBottomSheetStateDetails(), eventSink = eventsRecorder ), ) - rule.clickOn(textRes) + clickOn(textRes) eventsRecorder.assertList( listOf( MediaViewerEvent.OnNavigateTo(0), @@ -179,24 +178,25 @@ class MediaViewerViewTest { } @Test - fun `clicking on image hides the overlay`() { + fun `clicking on image hides the overlay`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMediaViewerState( eventSink = eventsRecorder ) - rule.setMediaViewerView( + setMediaViewerView( state = state, ) // Ensure that the action are visible - val contentDescription = rule.activity.getString(CommonStrings.action_share) - rule.onNodeWithContentDescription(contentDescription) + val resources = activity!!.resources + val contentDescription = resources.getString(CommonStrings.action_share) + onNodeWithContentDescription(contentDescription) .assertExists() .assertHasClickAction() - val imageContentDescription = rule.activity.getString(CommonStrings.common_image) - rule.onNodeWithContentDescription(imageContentDescription).performClick() + val imageContentDescription = resources.getString(CommonStrings.common_image) + onNodeWithContentDescription(imageContentDescription).performClick() // Give time for the animation (? since even by removing AnimatedVisibility it still fails) - rule.mainClock.advanceTimeBy(1_000) - rule.onNodeWithContentDescription(contentDescription) + mainClock.advanceTimeBy(1_000) + onNodeWithContentDescription(contentDescription) .assertDoesNotExist() eventsRecorder.assertList( listOf( @@ -207,19 +207,19 @@ class MediaViewerViewTest { } @Test - fun `clicking swipe on the image invokes the expected callback`() { + fun `clicking swipe on the image invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val state = aMediaViewerState( eventSink = eventsRecorder ) ensureCalledOnce { callback -> - rule.setMediaViewerView( + setMediaViewerView( state = state, onBackClick = callback, ) - val imageContentDescription = rule.activity.getString(CommonStrings.common_image) - rule.onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown(startY = centerY) } - rule.mainClock.advanceTimeBy(1_000) + val imageContentDescription = activity!!.getString(CommonStrings.common_image) + onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown(startY = centerY) } + mainClock.advanceTimeBy(1_000) } eventsRecorder.assertList( listOf( @@ -230,18 +230,18 @@ class MediaViewerViewTest { } @Test - fun `error case, click on retry emits the expected Event`() { + fun `error case, click on retry emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val data = aMediaViewerPageData( downloadedMedia = AsyncData.Failure(IllegalStateException("error")), ) - rule.setMediaViewerView( + setMediaViewerView( aMediaViewerState( listData = listOf(data), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_retry) + clickOn(CommonStrings.action_retry) eventsRecorder.assertList( listOf( MediaViewerEvent.OnNavigateTo(0), @@ -252,18 +252,18 @@ class MediaViewerViewTest { } @Test - fun `error case, click on cancel emits the expected Event`() { + fun `error case, click on cancel emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() val data = aMediaViewerPageData( downloadedMedia = AsyncData.Failure(IllegalStateException("error")), ) - rule.setMediaViewerView( + setMediaViewerView( aMediaViewerState( listData = listOf(data), eventSink = eventsRecorder ), ) - rule.clickOn(CommonStrings.action_cancel) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertList( listOf( MediaViewerEvent.OnNavigateTo(0), @@ -274,7 +274,7 @@ class MediaViewerViewTest { } } -private fun AndroidComposeTestRule.setMediaViewerView( +private fun AndroidComposeUiTest.setMediaViewerView( state: MediaViewerState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { 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 9a65ca0ad5..4840569c0e 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 @@ -6,12 +6,15 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.textcomposer.impl.components.markdown import android.widget.EditText import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.core.text.getSpans import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat @@ -32,66 +35,54 @@ 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 -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class MarkdownTextInputTest { - @get:Rule val rule = createAndroidComposeRule() - @Test - fun `when user types onTyping is triggered with value 'true'`() = runTest { + fun `when user types onTyping is triggered with value 'true'`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialFocus = true) val onTyping = EnsureCalledOnceWithParam(expectedParam = true, result = Unit) - rule.setMarkdownTextInput(state = state, onTyping = onTyping) - rule.activityRule.scenario.onActivity { - it.findEditor().setText("Test") - } - rule.awaitIdle() + setMarkdownTextInput(state = state, onTyping = onTyping) + activity!!.findEditor().setText("Test") + awaitIdle() onTyping.assertSuccess() } @Test - fun `when user removes text onTyping is triggered with value 'false'`() = runTest { + fun `when user removes text onTyping is triggered with value 'false'`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialFocus = true) val onTyping = EventsRecorder() - rule.setMarkdownTextInput(state = state, onTyping = onTyping) - rule.activityRule.scenario.onActivity { - val editText = it.findEditor() - editText.setText("Test") - editText.setText("") - editText.setText(null) - } - rule.awaitIdle() + setMarkdownTextInput(state = state, onTyping = onTyping) + val editText = activity!!.findEditor() + editText.setText("Test") + editText.setText("") + editText.setText(null) + awaitIdle() onTyping.assertList(listOf(true, false, false)) } @Test - fun `when user types something that's not a mention onSuggestionReceived is triggered with 'null'`() = runTest { + fun `when user types something that's not a mention onSuggestionReceived is triggered with 'null'`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialFocus = true) val onSuggestionReceived = EventsRecorder() - rule.setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived) - rule.activityRule.scenario.onActivity { - it.findEditor().setText("Test") - } - rule.awaitIdle() + setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived) + activity!!.findEditor().setText("Test") + awaitIdle() onSuggestionReceived.assertSingle(null) } @Test - fun `when user types something that's a mention onSuggestionReceived is triggered a real value`() = runTest { + fun `when user types something that's a mention onSuggestionReceived is triggered a real value`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialFocus = true) val onSuggestionReceived = EventsRecorder() - rule.setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived) - rule.activityRule.scenario.onActivity { - it.findEditor().setText("@") - it.findEditor().setText("#") - it.findEditor().setText("/") - } - rule.awaitIdle() + setMarkdownTextInput(state = state, onSuggestionReceived = onSuggestionReceived) + val editor = activity!!.findEditor() + editor.setText("@") + editor.setText("#") + editor.setText("/") + awaitIdle() onSuggestionReceived.assertList( listOf( // User mention suggestion @@ -105,69 +96,59 @@ class MarkdownTextInputTest { } @Test - fun `when the selection changes in the UI the state is updated`() = runTest { + fun `when the selection changes in the UI the state is updated`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = true) - rule.setMarkdownTextInput(state = state) - rule.activityRule.scenario.onActivity { - val editor = it.findEditor() - editor.setSelection(2) - } - rule.awaitIdle() + setMarkdownTextInput(state = state) + val editor = activity!!.findEditor() + editor.setSelection(2) + awaitIdle() // Selection is updated assertThat(state.selection).isEqualTo(2..2) } @Test - fun `when the selection state changes in the view is updated`() = runTest { + fun `when the selection state changes in the view is updated`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = true) - rule.setMarkdownTextInput(state = state) - var editor: EditText? = null - rule.activityRule.scenario.onActivity { - editor = it.findEditor() - state.selection = 2..2 - } - rule.awaitIdle() + setMarkdownTextInput(state = state) + val editor = activity!!.findEditor() + state.selection = 2..2 + awaitIdle() // Selection state is updated - assertThat(editor?.selectionStart).isEqualTo(2) - assertThat(editor?.selectionEnd).isEqualTo(2) + assertThat(editor.selectionStart).isEqualTo(2) + assertThat(editor.selectionEnd).isEqualTo(2) } @Test - fun `when the view focus changes the state is updated`() = runTest { + fun `when the view focus changes the state is updated`() = runAndroidComposeUiTest { val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = false) - rule.setMarkdownTextInput(state = state) - rule.activityRule.scenario.onActivity { - val editor = it.findEditor() - editor.requestFocus() - } + setMarkdownTextInput(state = state) + val editor = activity!!.findEditor() + editor.requestFocus() // Focus state is updated assertThat(state.hasFocus).isTrue() } @Test - fun `inserting a mention replaces the existing text with a span`() = runTest { + fun `inserting a mention replaces the existing text with a span`() = runAndroidComposeUiTest { val permalinkParser = FakePermalinkParser(result = { PermalinkData.UserLink(A_SESSION_ID) }) val state = aMarkdownTextEditorState(initialText = "@", initialFocus = true) state.currentSuggestion = Suggestion(0, 1, SuggestionType.Mention, "") - rule.setMarkdownTextInput(state = state) - var editor: EditText? = null - rule.activityRule.scenario.onActivity { - editor = it.findEditor() - state.insertSuggestion( - ResolvedSuggestion.Member(roomMember = aRoomMember()), - aMentionSpanProvider(permalinkParser), - ) - } - rule.awaitIdle() + setMarkdownTextInput(state = state) + val editor = activity!!.findEditor() + state.insertSuggestion( + ResolvedSuggestion.Member(roomMember = aRoomMember()), + aMentionSpanProvider(permalinkParser), + ) + awaitIdle() // Text is replaced with a placeholder - assertThat(editor?.editableText.toString()).isEqualTo("@ ") + assertThat(editor.editableText.toString()).isEqualTo("@ ") // The placeholder contains a MentionSpan - val mentionSpans = editor?.editableText?.getSpans(0, 2).orEmpty() + val mentionSpans = editor.editableText?.getSpans(0, 2).orEmpty() assertThat(mentionSpans).isNotEmpty() } - private fun AndroidComposeTestRule.setMarkdownTextInput( + private fun AndroidComposeUiTest.setMarkdownTextInput( state: MarkdownTextEditorState = aMarkdownTextEditorState(), onTyping: (Boolean) -> Unit = {}, onSuggestionReceived: (Suggestion?) -> Unit = {}, diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt index 0ba6c22710..0244673ea5 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsViewTest.kt @@ -6,60 +6,58 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.troubleshoot.impl import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) class TroubleshootNotificationsViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `press menu back invokes the expected callback`() { + fun `press menu back invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) ensureCalledOnce { - rule.setTroubleshootNotificationsView( + setTroubleshootNotificationsView( state = aTroubleshootNotificationsState( eventSink = eventsRecorder ), onBackClick = it, ) - rule.pressBack() + pressBack() } } @Test - fun `clicking on run test emits the expected Event`() { + fun `clicking on run test emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTroubleshootNotificationsView( + setTroubleshootNotificationsView( aTroubleshootNotificationsState( eventSink = eventsRecorder ), ) - rule.onNodeWithText("Run tests").performClick() + onNodeWithText("Run tests").performClick() eventsRecorder.assertSingle(TroubleshootNotificationsEvents.StartTests) } @Config(qualifiers = "h1024dp") @Test - fun `clicking on run test again emits the expected Event`() { + fun `clicking on run test again emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTroubleshootNotificationsView( + setTroubleshootNotificationsView( aTroubleshootNotificationsState( tests = listOf( aTroubleshootTestStateFailure( @@ -69,7 +67,7 @@ class TroubleshootNotificationsViewTest { eventSink = eventsRecorder ), ) - rule.onNodeWithText("Run tests again").performClick() + onNodeWithText("Run tests again").performClick() eventsRecorder.assertList( listOf( TroubleshootNotificationsEvents.RetryFailedTests, @@ -80,9 +78,9 @@ class TroubleshootNotificationsViewTest { @Config(qualifiers = "h1024dp") @Test - fun `clicking on quick fix emits the expected Event`() { + fun `clicking on quick fix emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setTroubleshootNotificationsView( + setTroubleshootNotificationsView( aTroubleshootNotificationsState( tests = listOf( aTroubleshootTestStateFailure( @@ -92,7 +90,7 @@ class TroubleshootNotificationsViewTest { eventSink = eventsRecorder ), ) - rule.onNodeWithText("Attempt to fix").performClick() + onNodeWithText("Attempt to fix").performClick() eventsRecorder.assertList( listOf( TroubleshootNotificationsEvents.RetryFailedTests, @@ -102,7 +100,7 @@ class TroubleshootNotificationsViewTest { } } -private fun AndroidComposeTestRule.setTroubleshootNotificationsView( +private fun AndroidComposeUiTest.setTroubleshootNotificationsView( state: TroubleshootNotificationsState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt index fa4e65ad9a..94cde37452 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryViewTest.kt @@ -6,14 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.libraries.troubleshoot.impl.history import androidx.activity.ComponentActivity -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_FORMATTED_DATE @@ -23,67 +26,62 @@ import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn -import org.junit.Rule import org.junit.Test -import org.junit.rules.TestRule import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PushHistoryViewTest { - @get:Rule - val rule = createAndroidComposeRule() - @Test - fun `clicking on Reset sends a PushHistoryEvents`() { + fun `clicking on Reset sends a PushHistoryEvents`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPushHistoryView( + setPushHistoryView( aPushHistoryState( pushCounter = 123, eventSink = eventsRecorder, ), ) - val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu) - rule.onNodeWithContentDescription(menuContentDescription).performClick() - rule.clickOn(CommonStrings.action_reset) + val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) + onNodeWithContentDescription(menuContentDescription).performClick() + clickOn(CommonStrings.action_reset) eventsRecorder.assertSingle(PushHistoryEvents.Reset(requiresConfirmation = true)) // Also check that the push counter is rendered - rule.onNodeWithText("123").assertExists() + onNodeWithText("123").assertExists() } @Test - fun `clicking on show only errors sends a PushHistoryEvents(true)`() { + fun `clicking on show only errors sends a PushHistoryEvents(true)`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPushHistoryView( + setPushHistoryView( aPushHistoryState( showOnlyErrors = false, eventSink = eventsRecorder, ), ) - val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu) - rule.onNodeWithContentDescription(menuContentDescription).performClick() - rule.onNodeWithText("Show only errors").performClick() + val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) + onNodeWithContentDescription(menuContentDescription).performClick() + onNodeWithText("Show only errors").performClick() eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = true)) } @Test - fun `clicking on show only errors sends a PushHistoryEvents(false)`() { + fun `clicking on show only errors sends a PushHistoryEvents(false)`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPushHistoryView( + setPushHistoryView( aPushHistoryState( showOnlyErrors = true, eventSink = eventsRecorder, ), ) - val menuContentDescription = rule.activity.getString(CommonStrings.a11y_user_menu) - rule.onNodeWithContentDescription(menuContentDescription).performClick() - rule.onNodeWithText("Show only errors").performClick() + val menuContentDescription = activity!!.getString(CommonStrings.a11y_user_menu) + onNodeWithContentDescription(menuContentDescription).performClick() + onNodeWithText("Show only errors").performClick() eventsRecorder.assertSingle(PushHistoryEvents.SetShowOnlyErrors(showOnlyErrors = false)) } @Test - fun `clicking on an invalid event has no effect`() { + fun `clicking on an invalid event has no effect`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) - rule.setPushHistoryView( + setPushHistoryView( aPushHistoryState( pushHistoryItems = listOf( aPushHistoryItem( @@ -93,14 +91,14 @@ class PushHistoryViewTest { eventSink = eventsRecorder, ), ) - rule.onNodeWithText(A_FORMATTED_DATE).performClick() + onNodeWithText(A_FORMATTED_DATE).performClick() // No callback invoked } @Test - fun `clicking on a valid event emits the expected Event`() { + fun `clicking on a valid event emits the expected Event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() - rule.setPushHistoryView( + setPushHistoryView( aPushHistoryState( pushHistoryItems = listOf( aPushHistoryItem( @@ -113,7 +111,7 @@ class PushHistoryViewTest { eventSink = eventsRecorder, ), ) - rule.onNodeWithText(A_FORMATTED_DATE).performClick() + onNodeWithText(A_FORMATTED_DATE).performClick() eventsRecorder.assertSingle( PushHistoryEvents.NavigateTo( sessionId = A_SESSION_ID, @@ -124,7 +122,7 @@ class PushHistoryViewTest { } } -private fun AndroidComposeTestRule.setPushHistoryView( +private fun AndroidComposeUiTest.setPushHistoryView( state: PushHistoryState, onBackClick: () -> Unit = EnsureNeverCalled(), ) { diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/RobolectricDispatcherCleaner.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/RobolectricDispatcherCleaner.kt index 12cfe44b44..d7ce9e2d28 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/RobolectricDispatcherCleaner.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/RobolectricDispatcherCleaner.kt @@ -6,15 +6,17 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.tests.testutils import androidx.activity.ComponentActivity import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import io.element.android.libraries.designsystem.utils.LocalUiTestMode import org.junit.Assert.assertFalse -import org.junit.rules.TestRule import kotlin.coroutines.CoroutineContext object RobolectricDispatcherCleaner { @@ -52,7 +54,7 @@ object RobolectricDispatcherCleaner { } } -fun AndroidComposeTestRule.setSafeContent( +fun AndroidComposeUiTest.setSafeContent( clearAndroidUiDispatcher: Boolean = false, content: @Composable () -> Unit, ) { diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt index d78f570a31..a473e6bd22 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt @@ -6,10 +6,14 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(ExperimentalTestApi::class) + package io.element.android.tests.testutils import androidx.activity.ComponentActivity import androidx.annotation.StringRes +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.assertIsDisplayed @@ -19,19 +23,17 @@ import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isDialog -import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import io.element.android.libraries.ui.strings.CommonStrings -import org.junit.rules.TestRule val trueMatcher = SemanticsMatcher("true matcher") { true } -fun AndroidComposeTestRule.clickOn( +fun AndroidComposeUiTest.clickOn( @StringRes res: Int, inDialog: Boolean = false, ) { - val text = activity.getString(res) + val text = activity!!.getString(res) onNode( hasText(text) and hasClickAction() and if (inDialog) hasAnyAncestor(isDialog()) else trueMatcher ) @@ -41,28 +43,28 @@ fun AndroidComposeTestRule.clickOn( /** * Press the back button in the app bar. */ -fun AndroidComposeTestRule.pressBack() { - val text = activity.getString(CommonStrings.action_back) +fun AndroidComposeUiTest.pressBack() { + val text = activity!!.getString(CommonStrings.action_back) onNode(hasContentDescription(text)).performClick() } /** * Press the back key. */ -fun AndroidComposeTestRule.pressBackKey() { - activity.onBackPressedDispatcher.onBackPressed() +fun AndroidComposeUiTest.pressBackKey() { + activity!!.onBackPressedDispatcher.onBackPressed() } fun SemanticsNodeInteractionsProvider.pressTag(tag: String) { onNode(hasTestTag(tag)).performClick() } -fun AndroidComposeTestRule.assertNoNodeWithText(@StringRes res: Int) { - val text = activity.getString(res) +fun AndroidComposeUiTest.assertNoNodeWithText(@StringRes res: Int) { + val text = activity!!.getString(res) onNodeWithText(text).assertDoesNotExist() } -fun AndroidComposeTestRule.assertNodeWithTextIsDisplayed(@StringRes res: Int) { - val text = activity.getString(res) +fun AndroidComposeUiTest.assertNodeWithTextIsDisplayed(@StringRes res: Int) { + val text = activity!!.getString(res) onNodeWithText(text).assertIsDisplayed() } From 6c7c48da698afb7daf5a6f7b23849110e8a5d9b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 16:59:14 +0200 Subject: [PATCH 068/471] Fix compilation issue --- .../android/features/call/ui/CallScreenViewTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt index fed9f90de0..e4f9c10a3c 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt @@ -18,9 +18,9 @@ import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.features.call.impl.pip.PictureInPictureEvents +import io.element.android.features.call.impl.pip.PictureInPictureEvent import io.element.android.features.call.impl.pip.aPictureInPictureState -import io.element.android.features.call.impl.ui.CallScreenEvents +import io.element.android.features.call.impl.ui.CallScreenEvent import io.element.android.features.call.impl.ui.CallScreenView import io.element.android.features.call.impl.ui.JavascriptBackHandler import io.element.android.features.call.impl.ui.aCallScreenState @@ -39,7 +39,7 @@ import org.robolectric.shadows.ShadowWebView class CallScreenViewTest { @Test fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() = runAndroidComposeUiTest { - val callEvents = EventsRecorder() + val callEvents = EventsRecorder() setCallScreenView( state = aCallScreenState(eventSink = callEvents), @@ -72,7 +72,7 @@ class CallScreenViewTest { @Config(shadows = [RecordingShadowWebView::class]) @Test fun `web view javascript back handler emits pip event when pip is supported`() = runAndroidComposeUiTest { - val pipEvents = EventsRecorder() + val pipEvents = EventsRecorder() setCallScreenView( state = aCallScreenState(), @@ -88,8 +88,8 @@ class CallScreenViewTest { } pipEvents.assertSize(2) - pipEvents.assertTrue(0) { it is PictureInPictureEvents.SetPipController } - pipEvents.assertTrue(1) { it is PictureInPictureEvents.EnterPictureInPicture } + pipEvents.assertTrue(0) { it is PictureInPictureEvent.SetPipController } + pipEvents.assertTrue(1) { it is PictureInPictureEvent.EnterPictureInPicture } } } From 194945c49e9d43489cca172e566715b24703460d Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 1 May 2026 12:19:17 +0000 Subject: [PATCH 069/471] Add `.clickable` to `RoomMemberModerationView` avatar --- .../roommembermoderation/impl/RoomMemberModerationView.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index ac6c5bc4cb..1a6a14d3f3 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -8,6 +8,7 @@ package io.element.android.features.roommembermoderation.impl +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -234,6 +235,12 @@ private fun RoomMemberActionsBottomSheet( modifier = Modifier .padding(bottom = 24.dp) .align(Alignment.CenterHorizontally) + .clickable { + coroutineScope.launch { + onSelectAction(ModerationAction.DisplayProfile, user) + bottomSheetState.hide() + } + } ) val bestName = user.getBestName() Text( From 715402d1788ef0f6ff7c2546a7cc4008b00e5c4d Mon Sep 17 00:00:00 2001 From: Strac Consulting Engineers Pty Ltd Date: Sat, 2 May 2026 09:40:23 +1000 Subject: [PATCH 070/471] Update AppDeveloperSettingsView.kt Fix 2 x Crash the app Fixes #6707 --- .../developer/appsettings/AppDeveloperSettingsView.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt index 71051cf829..f60eb2ac85 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt @@ -87,14 +87,7 @@ fun AppDeveloperSettingsView( onClick = onOpenShowkase ) } - PreferenceCategory(title = "Crash") { - ListItem( - headlineContent = { - Text("Crash the app 💥") - }, - onClick = { error("This crash is a test.") } - ) - } + RageshakePreferencesView( state = state.rageshakeState, ) From 0d2f71643aca180a7544de8c4958e5ff2ada6d62 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 4 May 2026 00:54:27 +0000 Subject: [PATCH 071/471] Sync Strings from Localazy --- .../src/main/res/values-ja/translations.xml | 4 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 4 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 3 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 8 +- .../src/main/res/values-da/translations.xml | 5 +- .../src/main/res/values-de/translations.xml | 6 +- .../src/main/res/values-fr/translations.xml | 6 +- .../src/main/res/values-hr/translations.xml | 1 + .../src/main/res/values-hu/translations.xml | 6 +- .../src/main/res/values-ja/translations.xml | 4 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 6 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 4 + .../src/main/res/values-hr/translations.xml | 4 + .../src/main/res/values-hu/translations.xml | 4 + .../src/main/res/values-be/translations.xml | 3 + .../src/main/res/values-cs/translations.xml | 3 + .../src/main/res/values-cy/translations.xml | 3 + .../src/main/res/values-da/translations.xml | 3 + .../src/main/res/values-de/translations.xml | 3 + .../src/main/res/values-el/translations.xml | 3 + .../src/main/res/values-es/translations.xml | 3 + .../src/main/res/values-et/translations.xml | 3 + .../src/main/res/values-eu/translations.xml | 3 + .../src/main/res/values-fa/translations.xml | 3 + .../src/main/res/values-fi/translations.xml | 3 + .../src/main/res/values-fr/translations.xml | 3 + .../src/main/res/values-hr/translations.xml | 3 + .../src/main/res/values-hu/translations.xml | 3 + .../src/main/res/values-in/translations.xml | 3 + .../src/main/res/values-it/translations.xml | 3 + .../src/main/res/values-ja/translations.xml | 5 +- .../src/main/res/values-ko/translations.xml | 3 + .../src/main/res/values-nb/translations.xml | 3 + .../src/main/res/values-nl/translations.xml | 3 + .../src/main/res/values-pl/translations.xml | 3 + .../main/res/values-pt-rBR/translations.xml | 3 + .../src/main/res/values-pt/translations.xml | 3 + .../src/main/res/values-ro/translations.xml | 3 + .../src/main/res/values-ru/translations.xml | 3 + .../src/main/res/values-sk/translations.xml | 3 + .../src/main/res/values-sv/translations.xml | 3 + .../src/main/res/values-tr/translations.xml | 3 + .../src/main/res/values-uk/translations.xml | 3 + .../src/main/res/values-ur/translations.xml | 3 + .../src/main/res/values-uz/translations.xml | 3 + .../main/res/values-zh-rTW/translations.xml | 3 + .../src/main/res/values-zh/translations.xml | 3 + .../src/main/res/values-da/translations.xml | 1 + .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 4 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-be/translations.xml | 2 +- .../src/main/res/values-bg/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-cy/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 12 +- .../src/main/res/values-de/translations.xml | 4 +- .../src/main/res/values-el/translations.xml | 2 +- .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-eu/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 4 +- .../src/main/res/values-fi/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 4 +- .../src/main/res/values-hr/translations.xml | 2 +- .../src/main/res/values-hu/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 30 +- .../src/main/res/values-ka/translations.xml | 2 +- .../src/main/res/values-ko/translations.xml | 2 +- .../src/main/res/values-lt/translations.xml | 2 +- .../src/main/res/values-nb/translations.xml | 2 +- .../src/main/res/values-nl/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 2 +- .../main/res/values-pt-rBR/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 4 +- .../src/main/res/values-ro/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-sv/translations.xml | 2 +- .../src/main/res/values-tr/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 2 +- .../src/main/res/values-ur/translations.xml | 2 +- .../src/main/res/values-uz/translations.xml | 2 +- .../src/main/res/values-vi/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 8 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-de/translations.xml | 10 +- .../src/main/res/values-fa/translations.xml | 10 +- .../src/main/res/values-in/translations.xml | 10 +- .../src/main/res/values-pt/translations.xml | 26 +- .../src/main/res/values-zh/translations.xml | 8 +- .../src/main/res/values-da/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-da/translations.xml | 8 + .../src/main/res/values-hr/translations.xml | 9 + .../src/main/res/values-hu/translations.xml | 8 + .../src/main/res/values-ja/translations.xml | 10 +- .../src/main/res/values-zh/translations.xml | 6 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-da/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 4 +- .../src/main/res/values-zh/translations.xml | 8 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 12 +- .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 14 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 6 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-pt/translations.xml | 4 +- .../src/main/res/values-zh/translations.xml | 6 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-da/translations.xml | 2 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-hr/translations.xml | 1 + .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 1 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-da/translations.xml | 28 +- .../src/main/res/values-de/translations.xml | 7 +- .../src/main/res/values-fa/translations.xml | 4 +- .../src/main/res/values-fr/translations.xml | 5 + .../src/main/res/values-hr/translations.xml | 15 + .../src/main/res/values-hu/translations.xml | 7 + .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 8 +- .../src/main/res/values-pt/translations.xml | 8 +- .../src/main/res/values-zh/translations.xml | 28 +- .../src/main/res/values/localazy.xml | 1 + ...nfigureroom_ConfigureRoomViewDark_7_de.png | 4 +- ...figureroom_ConfigureRoomViewLight_7_de.png | 4 +- ..._SelectParentSpaceBottomSheet_Day_0_de.png | 4 +- ...hooseSelfVerificationModeView_Day_0_de.png | 4 +- ...hooseSelfVerificationModeView_Day_1_de.png | 4 +- ...hooseSelfVerificationModeView_Day_2_de.png | 4 +- ...hooseSelfVerificationModeView_Day_3_de.png | 4 +- ...hooseSelfVerificationModeView_Day_4_de.png | 4 +- ....roomlist_RoomListContextMenu_Day_0_de.png | 3 + ....roomlist_RoomListContextMenu_Day_1_de.png | 3 + ....roomlist_RoomListContextMenu_Day_2_de.png | 3 + ...mListDeclineInviteMenuContent_Day_0_de.png | 3 - ...ist_RoomListDeclineInviteMenu_Day_0_de.png | 3 + ...ist_RoomListDeclineInviteMenu_Day_1_de.png | 3 + ...ist_RoomListDeclineInviteMenu_Day_2_de.png | 3 + ...omListModalBottomSheetContent_Day_0_de.png | 3 - ...omListModalBottomSheetContent_Day_1_de.png | 3 - ...omListModalBottomSheetContent_Day_2_de.png | 3 - ...irmation_CodeConfirmationView_Day_0_de.png | 3 + ....impl.screens.error_ErrorView_Day_2_de.png | 4 +- ....impl.screens.error_ErrorView_Day_3_de.png | 4 +- ....impl.screens.error_ErrorView_Day_4_de.png | 4 +- ....impl.screens.error_ErrorView_Day_8_de.png | 3 + ...ns.root_LinkNewDeviceRootView_Day_0_de.png | 4 +- ...ns.root_LinkNewDeviceRootView_Day_1_de.png | 4 +- ...ns.root_LinkNewDeviceRootView_Day_2_de.png | 4 +- ...ns.root_LinkNewDeviceRootView_Day_3_de.png | 4 +- ...ns.root_LinkNewDeviceRootView_Day_4_de.png | 4 +- ...ns.root_LinkNewDeviceRootView_Day_5_de.png | 4 +- ....impl.share_ShareLocationView_Day_3_de.png | 4 +- ...on.impl.show_ShowLocationView_Day_5_de.png | 4 +- ...changeserver_ChangeServerView_Day_5_de.png | 4 +- ...ogin.impl.login_LoginModeView_Day_5_de.png | 4 +- ...mation_QrCodeConfirmationView_Day_0_de.png | 4 +- ....qrcode.error_QrCodeErrorView_Day_2_de.png | 4 +- ....qrcode.error_QrCodeErrorView_Day_3_de.png | 4 +- ....qrcode.error_QrCodeErrorView_Day_5_de.png | 4 +- ....qrcode.intro_QrCodeIntroView_Day_0_de.png | 4 +- ....impl_AccountDeactivationView_Day_0_de.png | 4 +- ....impl_AccountDeactivationView_Day_1_de.png | 4 +- ....impl_AccountDeactivationView_Day_2_de.png | 4 +- ....impl_AccountDeactivationView_Day_3_de.png | 4 +- ....impl_AccountDeactivationView_Day_4_de.png | 4 +- ...atures.logout.impl_LogoutView_Day_1_de.png | 4 +- ...atures.logout.impl_LogoutView_Day_3_de.png | 4 +- ...atures.logout.impl_LogoutView_Day_7_de.png | 4 +- ...atures.logout.impl_LogoutView_Day_8_de.png | 4 +- ...atures.logout.impl_LogoutView_Day_9_de.png | 4 +- ...ent_TimelineItemEncryptedView_Day_6_de.png | 4 +- ...sable_SecureBackupDisableView_Day_0_de.png | 4 +- ...sable_SecureBackupDisableView_Day_1_de.png | 4 +- ...sable_SecureBackupDisableView_Day_2_de.png | 4 +- ...sable_SecureBackupDisableView_Day_3_de.png | 4 +- ...ureBackupEnterRecoveryKeyView_Day_0_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_0_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_10_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_11_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_12_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_13_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_14_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_15_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_16_de.png | 4 +- ...pl.root_SecureBackupRootView_Day_17_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_1_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_2_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_3_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_4_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_5_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_6_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_7_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_8_de.png | 4 +- ...mpl.root_SecureBackupRootView_Day_9_de.png | 4 +- ...p_SecureBackupSetupViewChange_Day_0_de.png | 4 +- ...p_SecureBackupSetupViewChange_Day_1_de.png | 4 +- ...p_SecureBackupSetupViewChange_Day_5_de.png | 4 +- ...l.setup_SecureBackupSetupView_Day_0_de.png | 4 +- ...l.setup_SecureBackupSetupView_Day_1_de.png | 4 +- ...l.setup_SecureBackupSetupView_Day_5_de.png | 4 +- ...ing_IncomingVerificationView_Day_11_de.png | 4 +- ...ming_IncomingVerificationView_Day_2_de.png | 4 +- ...ming_IncomingVerificationView_Day_4_de.png | 4 +- ...ing_OutgoingVerificationView_Day_11_de.png | 4 +- screenshots/html/data.js | 2112 +++++++++-------- ...changeserver_ChangeServerView_Day_5_en.png | 4 +- ...angeserver_ChangeServerView_Night_5_en.png | 4 +- ...ogin.impl.login_LoginModeView_Day_5_en.png | 4 +- ...in.impl.login_LoginModeView_Night_5_en.png | 4 +- 245 files changed, 1664 insertions(+), 1442 deletions(-) create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png delete mode 100644 screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_de.png create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_de.png create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_de.png create mode 100644 screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_de.png delete mode 100644 screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_de.png delete mode 100644 screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_de.png delete mode 100644 screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_de.png create mode 100644 screenshots/de/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_de.png create mode 100644 screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_8_de.png diff --git a/features/analytics/api/src/main/res/values-ja/translations.xml b/features/analytics/api/src/main/res/values-ja/translations.xml index 5554ee162f..e1495271d3 100644 --- a/features/analytics/api/src/main/res/values-ja/translations.xml +++ b/features/analytics/api/src/main/res/values-ja/translations.xml @@ -1,7 +1,7 @@ - "問題発見のため、匿名の使用データの共有にご協力ください。" - "利用規約の全文を%1$sから確認することができます。" + "改善のため、匿名の使用データの共有にご協力ください。" + "規約の全文は%1$sから確認することができます。" "こちら" "使用データを共有" diff --git a/features/analytics/api/src/main/res/values-zh/translations.xml b/features/analytics/api/src/main/res/values-zh/translations.xml index e8f0fcb434..8f1c1699d9 100644 --- a/features/analytics/api/src/main/res/values-zh/translations.xml +++ b/features/analytics/api/src/main/res/values-zh/translations.xml @@ -1,7 +1,7 @@ "共享匿名使用数据以帮助我们排查问题。" - "你可以阅读我们的所有条款 %1$s。" + "你可以点击 %1$s 阅读我们的所有条款。" "此处" "共享分析数据" diff --git a/features/analytics/impl/src/main/res/values-ja/translations.xml b/features/analytics/impl/src/main/res/values-ja/translations.xml index 2cee69962c..162e01ecb0 100644 --- a/features/analytics/impl/src/main/res/values-ja/translations.xml +++ b/features/analytics/impl/src/main/res/values-ja/translations.xml @@ -1,8 +1,8 @@ "いかなる個人情報も記録, 分析されることはありません" - "問題発見のため、匿名の使用データの共有にご協力ください。" - "利用規約の全文を%1$sから確認することができます。" + "改善のため、匿名の使用データの共有にご協力ください。" + "規約の全文は%1$sから確認することができます。" "こちら" "いつでも設定は変更できます" "情報が第三者に共有されることはありません" diff --git a/features/analytics/impl/src/main/res/values-zh/translations.xml b/features/analytics/impl/src/main/res/values-zh/translations.xml index 8393e45e81..678d506287 100644 --- a/features/analytics/impl/src/main/res/values-zh/translations.xml +++ b/features/analytics/impl/src/main/res/values-zh/translations.xml @@ -2,7 +2,7 @@ "我们不会记录或分析任何个人数据" "共享匿名使用数据以帮助我们排查问题。" - "你可以阅读我们的所有条款 %1$s。" + "你可以点击 %1$s 阅读我们的所有条款。" "此处" "可以随时关闭此功能" "我们不会与第三方共享你的数据" diff --git a/features/createroom/impl/src/main/res/values-da/translations.xml b/features/createroom/impl/src/main/res/values-da/translations.xml index 66c4f08b70..ab72755dcf 100644 --- a/features/createroom/impl/src/main/res/values-da/translations.xml +++ b/features/createroom/impl/src/main/res/values-da/translations.xml @@ -19,7 +19,7 @@ Du kan ændre dette når som helst i rummets indstillinger." "Anmod om at deltage" "Kun inviterede brugere kan deltage." "Privat" - "Alle kan deltage i dette rum" + "Alle kan deltage." "Offentlig" "Alle i %1$s kan deltage." "Standard" diff --git a/features/createroom/impl/src/main/res/values-de/translations.xml b/features/createroom/impl/src/main/res/values-de/translations.xml index 4408bf66a3..c3f42ca287 100644 --- a/features/createroom/impl/src/main/res/values-de/translations.xml +++ b/features/createroom/impl/src/main/res/values-de/translations.xml @@ -28,7 +28,8 @@ Du kannst dies jederzeit in den Einstellungen des Chats ändern." "Adresse" " Sichtbarkeit des Chats" "(kein Space)" - "Home" + "Nicht zu einem Space hinzufügen" + "Kein Space ausgewählt" "Space hinzufügen" "Thema (optional)" "Beschreibung hinzufügen…" diff --git a/features/createroom/impl/src/main/res/values-fa/translations.xml b/features/createroom/impl/src/main/res/values-fa/translations.xml index 27542ccc19..821d55af67 100644 --- a/features/createroom/impl/src/main/res/values-fa/translations.xml +++ b/features/createroom/impl/src/main/res/values-fa/translations.xml @@ -3,7 +3,7 @@ "اتاق جدید" "دعوت افراد" "هنگام ایجاد اتاق خطایی رخ داد" - "تنها افراد دعوت شده می‌توانند به این اتاق دسترسی داشته باشند. همهٔ پیام‌ها رمزنگاری سرتاسری شده‌اند." + "تنها افراد دعوت شده می‌توانند بپیوندند." "هرکسی می‌تواند اتاق را بیابد. می‌توانید بعداً در تظیمات اتاق عوضش کنید." "درخواست دعوت" diff --git a/features/createroom/impl/src/main/res/values-zh/translations.xml b/features/createroom/impl/src/main/res/values-zh/translations.xml index 8d898fe5f0..1ba1036634 100644 --- a/features/createroom/impl/src/main/res/values-zh/translations.xml +++ b/features/createroom/impl/src/main/res/values-zh/translations.xml @@ -1,13 +1,13 @@ "新房间" - "邀请朋友" + "邀请人员" "创建房间时出错" "由于未知错误,空间创建失败。请稍后再试。" "添加名称…" "新房间" "新空间" - "仅限受邀者加入。" + "仅限受邀人员加入。" "私密" "任何人都能找到此房间。 你可以随时在房间设置中更改。" @@ -17,11 +17,11 @@ "申请加入" "%1$s 中的任何人都可以加入,但其他人必须申请访问。" "申请加入" - "仅限受邀者加入。" + "仅限受邀人员加入。" "私密" "任何人都可以加入。" "公共" - "%1$s 中的任何人可加入。" + "%1$s 中的任何人都可以加入。" "标准" "谁有权访问此房间" "要使该房间在公共目录中可见,你需要一个地址。" diff --git a/features/deactivation/impl/src/main/res/values-da/translations.xml b/features/deactivation/impl/src/main/res/values-da/translations.xml index dfbf00a1c6..f434547b8a 100644 --- a/features/deactivation/impl/src/main/res/values-da/translations.xml +++ b/features/deactivation/impl/src/main/res/values-da/translations.xml @@ -1,13 +1,14 @@ - "Bekræft venligst, at du vil deaktivere din konto. Denne handling kan ikke fortrydes." + "Bekræft venligst, at du ønsker at slette din konto. Denne handling kan ikke fortrydes." "Slet alle mine beskeder" "Advarsel: Fremtidige brugere kan muligvis se ufuldstændige samtaler." - "Deaktivering af din konto er %1$s, det vil:" + "Sletning af din konto er %1$s, det vil:" "irreversibel" "%1$s din konto (du kan ikke logge ind igen, og dit ID kan ikke genbruges)." "Permanent deaktivere" "Fjerne dig fra alle samtaler" "Slette dine kontooplysninger fra vores identitetsserver." "Dine beskeder vil stadig være synlige for registrerede brugere, men vil ikke være tilgængelige for nye eller uregistrerede brugere, hvis du vælger at slette dem." + "Slet konto" diff --git a/features/deactivation/impl/src/main/res/values-de/translations.xml b/features/deactivation/impl/src/main/res/values-de/translations.xml index e03d53bbce..8430134d00 100644 --- a/features/deactivation/impl/src/main/res/values-de/translations.xml +++ b/features/deactivation/impl/src/main/res/values-de/translations.xml @@ -1,14 +1,14 @@ - "Bitte bestätige, dass du dein Konto deaktivieren möchtest. Dies kann nicht rückgängig gemacht werden." + "Bitte bestätige, dass du dein Konto löschen möchtest. Diese Aktion kann nicht rückgängig gemacht werden." "Lösche alle meine Nachrichten" "Warnung: Künftigen Nutzern werden möglicherweise unvollständige Konversationen angezeigt." - "Dein Konto zu deaktivieren ist %1$s. Folgendes wird passieren:" + "Das Löschen deines Kontos ist %1$s. Es wird:" "irreversibel" "%1$s dein Konto (du kannst dich nicht erneut anmelden und deine ID kann nicht wiederverwendet werden)." "Dauerhaft deaktivieren" "Du wirst aus allen Chats entfernt." "Lösche deine Kontoinformationen von unserem Identitätsserver." "Deine Nachrichten werden für bereits registrierte Nutzer weiterhin sichtbar sein. Für neue oder unregistrierte Nutzer sind sie nicht verfügbar, wenn du sie löschen solltest." - "Benutzerkonto deaktivieren" + "Konto löschen" diff --git a/features/deactivation/impl/src/main/res/values-fr/translations.xml b/features/deactivation/impl/src/main/res/values-fr/translations.xml index 875142bc01..cf69cb3275 100644 --- a/features/deactivation/impl/src/main/res/values-fr/translations.xml +++ b/features/deactivation/impl/src/main/res/values-fr/translations.xml @@ -1,14 +1,14 @@ - "Veuillez confirmer que vous souhaitez désactiver votre compte. Cette action ne peut pas être annulée." + "Veuillez confirmer que vous souhaitez supprimer votre compte. Cette action ne peut pas être annulée." "Supprimer tous mes messages" "Attention : les futurs utilisateurs pourraient voir des conversations incomplètes." - "La désactivation de votre compte est %1$s, cela va :" + "La suppression de votre compte est %1$s, cela va :" "irréversible" "%1$s votre compte (vous ne pourrez plus vous reconnecter et votre identifiant ne pourra pas être réutilisé)." "Désactiver définitivement" "Vous retirer de tous les salons et toutes les discussions." "Supprimer les informations de votre compte du serveur d’identité." "Rendre vos messages invisibles aux futurs membres des salons si vous choisissez de les supprimer. Vos messages seront toujours visibles pour les utilisateurs qui les ont déjà récupérés." - "Désactiver votre compte" + "Supprimer le compte" diff --git a/features/deactivation/impl/src/main/res/values-hr/translations.xml b/features/deactivation/impl/src/main/res/values-hr/translations.xml index 9273254886..1d8a02a08c 100644 --- a/features/deactivation/impl/src/main/res/values-hr/translations.xml +++ b/features/deactivation/impl/src/main/res/values-hr/translations.xml @@ -10,4 +10,5 @@ "Ukloniti vas iz svih soba za razgovore." "Izbrisati podatke o vašem računu s našeg poslužitelja identiteta." "Vaše će poruke i dalje biti vidljive registriranim korisnicima, ali neće biti dostupne novim ili neregistriranim korisnicima ako ih odlučite izbrisati." + "Izbriši račun" diff --git a/features/deactivation/impl/src/main/res/values-hu/translations.xml b/features/deactivation/impl/src/main/res/values-hu/translations.xml index 3d3722b8ef..2c3f51ed7a 100644 --- a/features/deactivation/impl/src/main/res/values-hu/translations.xml +++ b/features/deactivation/impl/src/main/res/values-hu/translations.xml @@ -1,14 +1,14 @@ - "Erősítse meg, hogy deaktiválja a fiókját. Ez a művelet nem vonható vissza." + "Erősítse meg a fiókja törlését. Ez a művelet nem vonható vissza." "Összes saját üzenet törlése" "Figyelmeztetés: A jövőbeli felhasználók hiányos beszélgetéseket láthatnak." - "A fiók deaktiválása %1$s, a következőket okozza:" + "Fiókjának törlése: %1$s, ez a következőket eredményezi:" "visszafordíthatatlan" "%1$s a fiókját (nem fog tudni újra bejelentkezni, és az azonosítója nem használható újra)." "Véglegesen letiltja" "Eltávolításra kerül az összes csevegőszobából." "Törlésre kerülnek a fiókadatai az azonosítási kiszolgálónkról." "Üzenetei továbbra is láthatóak maradnak a regisztrált felhasználók számára, de nem lesznek elérhetőek az új vagy nem regisztrált felhasználók számára, ha úgy dönt, hogy törli őket." - "Fiók deaktiválása" + "Fiók törlése" diff --git a/features/deactivation/impl/src/main/res/values-ja/translations.xml b/features/deactivation/impl/src/main/res/values-ja/translations.xml index f41dd1d282..53893a594f 100644 --- a/features/deactivation/impl/src/main/res/values-ja/translations.xml +++ b/features/deactivation/impl/src/main/res/values-ja/translations.xml @@ -1,9 +1,9 @@ - "アカウントを無効化することを再度確認します。この操作は元に戻せません。" + "アカウントを削除しようとしていることを確認しています。この操作は元に戻せません。" "メッセージをすべて削除" "注意: 新しいユーザーには断片的な会話が表示されます" - "アカウントを無効化することは %1$s であり、次の変化が生じます:" + "アカウントを削除することは %1$s であり、次の変化が生じます:" "不可逆" "アカウントを %1$s (再度ログイン不可, 同一のIDを再利用不可)" "恒久的に無効化する" diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml index 241f73516e..0e83b29e35 100644 --- a/features/ftue/impl/src/main/res/values-de/translations.xml +++ b/features/ftue/impl/src/main/res/values-de/translations.xml @@ -3,7 +3,7 @@ "Bestätigung unmöglich?" "Erstelle einen neuen Wiederherstellungsschlüssel" "Wähle eine Verifizierungsmethode, um den sicheren Nachrichtenversand einzurichten." - "Bestätige deine Identität" + "Bestätige deine digitale Identität" "Ein anderes Gerät verwenden" "Wiederherstellungsschlüssel verwenden" "Du kannst jetzt verschlüsselte Nachrichten lesen und versenden. Dein Chatpartner vertraut nun diesem Gerät." diff --git a/features/ftue/impl/src/main/res/values-ja/translations.xml b/features/ftue/impl/src/main/res/values-ja/translations.xml index 68b69079ef..9a87a3dcfa 100644 --- a/features/ftue/impl/src/main/res/values-ja/translations.xml +++ b/features/ftue/impl/src/main/res/values-ja/translations.xml @@ -11,5 +11,5 @@ "他の端末を使用" "一方の端末を待機中…" "設定は後で変更することができます。" - "メッセージを見逃さないため通知を許可" + "メッセージを見逃さないために通知を許可しましょう" diff --git a/features/ftue/impl/src/main/res/values-pt/translations.xml b/features/ftue/impl/src/main/res/values-pt/translations.xml index 5b6729f04e..34f39c7bdb 100644 --- a/features/ftue/impl/src/main/res/values-pt/translations.xml +++ b/features/ftue/impl/src/main/res/values-pt/translations.xml @@ -3,7 +3,7 @@ "Não é possível confirmar?" "Criar uma nova chave de recuperação" "Verifica este dispositivo para configurar o envio seguro de mensagens." - "Confirma que és tu" + "Confirma a tua identidade digital" "Utilizar outro dispositivo" "Utilizar chave de recuperação" "Agora podes ler ou enviar mensagens de forma segura, e qualquer pessoa com quem converses também pode confiar neste dispositivo." diff --git a/features/ftue/impl/src/main/res/values-zh/translations.xml b/features/ftue/impl/src/main/res/values-zh/translations.xml index c111aecaa9..f9171c9879 100644 --- a/features/ftue/impl/src/main/res/values-zh/translations.xml +++ b/features/ftue/impl/src/main/res/values-zh/translations.xml @@ -9,7 +9,7 @@ "现在你可以安全地读取或发送消息,并且与你聊天的任何人也可以信任此设备。" "设备已验证" "使用其它设备" - "正在等待其它设备……" + "正在等待其它设备…" "你可以稍后更改设置。" "允许通知,绝不错过任何消息" diff --git a/features/home/impl/src/main/res/values-pt/translations.xml b/features/home/impl/src/main/res/values-pt/translations.xml index 9870014904..6c6a72930e 100644 --- a/features/home/impl/src/main/res/values-pt/translations.xml +++ b/features/home/impl/src/main/res/values-pt/translations.xml @@ -6,7 +6,7 @@ "O toque de notificação foi atualizado — mais claro, mais rápido e menos perturbador." "Atualizámos os seus sons" "Recupera a tua identidade criptográfica e o histórico de mensagens com uma chave de recuperação se tiveres perdido todos os teus dispositivos existentes." - "Configurar recuperação" + "Chave de recuperação" "Configurar a 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" diff --git a/features/home/impl/src/main/res/values-zh/translations.xml b/features/home/impl/src/main/res/values-zh/translations.xml index 4bc15b08cf..39900a6f0b 100644 --- a/features/home/impl/src/main/res/values-zh/translations.xml +++ b/features/home/impl/src/main/res/values-zh/translations.xml @@ -21,14 +21,14 @@ "你确定要拒绝与 %1$s 私聊?" "拒绝聊天" "没有邀请" - "%1$s (%2$s)邀请了你" + "%1$s(%2$s)邀请了你" "此为一次性流程,感谢等待。" "设置账户。" "创建新的对话或房间" "清除筛选条件" "通过向某人发送消息来开始。" "暂无聊天。" - "收藏夹" + "收藏" "可以在聊天设置里将聊天添加到收藏夹。 现在可以取消选择筛选器以查看其它对话。" "你尚未收藏任何聊天" @@ -38,7 +38,7 @@ "你暂无任何低优先级聊天" "你可以取消选择筛选器以查看其它对话" "你暂无适用于此选项的聊天" - "用户" + "人员" "你暂无任何私聊" "房间" "你尚未进入任何房间" diff --git a/features/invite/impl/src/main/res/values-zh/translations.xml b/features/invite/impl/src/main/res/values-zh/translations.xml index 61e7f15f59..02825bca6d 100644 --- a/features/invite/impl/src/main/res/values-zh/translations.xml +++ b/features/invite/impl/src/main/res/values-zh/translations.xml @@ -10,7 +10,7 @@ "你确定要拒绝与 %1$s 私聊?" "拒绝聊天" "没有邀请" - "%1$s (%2$s)邀请了你" + "%1$s(%2$s)邀请了你" "是,拒绝并屏蔽" "你确定要拒绝此房间的加入邀请?这也将阻止 %1$s 与你联系或邀请你加入房间。" "拒绝邀请并屏蔽" diff --git a/features/invitepeople/impl/src/main/res/values-da/translations.xml b/features/invitepeople/impl/src/main/res/values-da/translations.xml index fbb1814e9f..1754ef6e0d 100644 --- a/features/invitepeople/impl/src/main/res/values-da/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-da/translations.xml @@ -2,4 +2,8 @@ "Allerede medlem" "Allerede inviteret" + "Du har i øjeblikket ingen chats med disse kontakter. Bekræft deres invitation til dette rum, før du fortsætter." + "Du har i øjeblikket ingen chats med denne kontakt. Bekræft deres invitation til dette rum, før du fortsætter." + "Inviter nye kontakter til dette rum?" + "Inviter ny kontakt til dette rum?" diff --git a/features/invitepeople/impl/src/main/res/values-hr/translations.xml b/features/invitepeople/impl/src/main/res/values-hr/translations.xml index 66031c5fd7..471b79d709 100644 --- a/features/invitepeople/impl/src/main/res/values-hr/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-hr/translations.xml @@ -2,4 +2,8 @@ "Već je član" "Već je pozvan/a" + "Trenutno nemate razgovora s ovim kontaktima. Potvrdite da ih želite pozvati u ovu sobu prije nego što nastavite." + "Trenutno nemate razgovora s ovim kontaktom. Potvrdite da ste ga pozvali u ovu sobu prije nego što nastavite." + "Pozvati nove kontakte u ovu sobu?" + "Pozvati novi kontakt u ovu sobu?" diff --git a/features/invitepeople/impl/src/main/res/values-hu/translations.xml b/features/invitepeople/impl/src/main/res/values-hu/translations.xml index 16f35b018c..de2cab0d73 100644 --- a/features/invitepeople/impl/src/main/res/values-hu/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-hu/translations.xml @@ -2,4 +2,8 @@ "Már tag" "Már meghívták" + "Jelenleg nincsenek csevegései ezekkel a kapcsolatokkal. Mielőtt továbbmenne, erősítse meg, hogy meghívja őket ebbe a szobába." + "Jelenleg nincsenek beszélgetései ezzel a személlyel. A folytatás előtt erősítse meg, hogy meghívja ebbe a szobába." + "Új személyeket hív meg ebbe a szobába?" + "Új személyt hív meg ebbe a szobába?" diff --git a/features/linknewdevice/impl/src/main/res/values-be/translations.xml b/features/linknewdevice/impl/src/main/res/values-be/translations.xml index 16372fa6e4..378a405c95 100644 --- a/features/linknewdevice/impl/src/main/res/values-be/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-be/translations.xml @@ -17,6 +17,8 @@ "Калі вы сутыкнуліся з той жа праблемай, паспрабуйце іншую сетку Wi-Fi або скарыстайцеся мабільнымі дадзенымі замест Wi-Fi." "Калі гэта не дапамагло, увайдзіце ўручную" "Злучэнне небяспечнае" + "Вам будзе прапанавана ўвесці дзве лічбы, паказаныя на гэтай прыладзе." + "Увядзіце наступны нумар на іншай прыладзе." "Уваход быў адменены на іншай прыладзе." "Запыт на ўваход скасаваны" "Уваход на іншай прыладзе быў адхілены." @@ -35,4 +37,5 @@ "Каб працягнуць, вам неабходна дазволіць %1$s выкарыстоўваць камеру вашай прылады." "Дазвольце доступ да камеры для сканіравання QR-кода" "Адбылася нечаканая памылка. Калі ласка, паспрабуйце яшчэ раз." + "У чаканні іншай прылады" diff --git a/features/linknewdevice/impl/src/main/res/values-cs/translations.xml b/features/linknewdevice/impl/src/main/res/values-cs/translations.xml index 4b8f230d55..e0150668a3 100644 --- a/features/linknewdevice/impl/src/main/res/values-cs/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-cs/translations.xml @@ -34,6 +34,8 @@ "Pokud narazíte na stejný problém, zkuste jinou síť wifi nebo použijte mobilní data místo wifi" "Pokud to nefunguje, přihlaste se ručně" "Připojení není zabezpečené" + "Budete požádáni o zadání dvou níže uvedených číslic." + "Zadejte níže uvedené číslo na svém dalším zařízení" "Přihlášení bylo na druhém zařízení zrušeno." "Žádost o přihlášení zrušena" "Přihlášení bylo na druhém zařízení odmítnuto." @@ -54,4 +56,5 @@ Zkuste se přihlásit ručně nebo naskenujte QR kód pomocí jiného zařízen "Abyste mohli pokračovat, musíte aplikaci %1$s udělit povolení k použití kamery vašeho zařízení." "Povolte přístup k fotoaparátu a naskenujte QR kód" "Vyskytla se neočekávaná chyba. Prosím zkuste to znovu." + "Čekání na vaše další zařízení" diff --git a/features/linknewdevice/impl/src/main/res/values-cy/translations.xml b/features/linknewdevice/impl/src/main/res/values-cy/translations.xml index b26aed52ef..6b1cb7781f 100644 --- a/features/linknewdevice/impl/src/main/res/values-cy/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-cy/translations.xml @@ -17,6 +17,8 @@ "Os ydych chi\'n dod ar draws yr un broblem, rhowch gynnig ar rwydwaith wifi gwahanol neu defnyddiwch eich data symudol yn lle wifi" "Os nad yw hynny\'n gweithio, mewngofnodwch â llaw" "Nid yw\'r cysylltiad yn ddiogel" + "Bydd gofyn i chi nodi\'r ddau ddigid sy\'n cael eu dangos ar y ddyfais hon." + "Rhowch y rhif isod ar eich dyfais arall" "Cafodd y mewngofnodi ei ddiddymu ar y ddyfais arall." "Cais mewngofnodi wedi\'i ddiddymu" "Cafodd y mewngofnodi ar y ddyfais arall ei wrthod." @@ -35,4 +37,5 @@ Ceisiwch fewngofnodi â llaw, neu sganiwch y cod QR gyda dyfais arall." "Mae angen i chi roi caniatâd i %1$s ddefnyddio camera eich dyfais er mwyn parhau." "Caniatáu mynediad camera i sganio\'r cod QR" "Digwyddodd gwall annisgwyl. Ceisiwch eto." + "Yn aros am eich dyfais arall" diff --git a/features/linknewdevice/impl/src/main/res/values-da/translations.xml b/features/linknewdevice/impl/src/main/res/values-da/translations.xml index 5bc9f04fb9..45f510e90b 100644 --- a/features/linknewdevice/impl/src/main/res/values-da/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-da/translations.xml @@ -34,6 +34,8 @@ "Hvis du støder på det samme problem, kan du prøve et andet wifi-netværk eller bruge dine mobildata i stedet for wifi" "Hvis det ikke virker, skal du logge ind manuelt" "Forbindelsen er ikke sikker" + "Du bliver bedt om at indtaste de to cifre, der vises på denne enhed." + "Indtast nummeret herunder på din anden enhed" "Login blev annulleret på den anden enhed." "Anmodning om login annulleret" "Login blev afvist på den anden enhed." @@ -54,4 +56,5 @@ Prøv at logge ind manuelt, eller scan QR-koden med en anden enhed." "Du skal give tilladelse til at %1$s kan benytte enhedens kamera, for at fortsætte." "Tillad kameraadgang for at scanne QR-koden" "Der opstod en uventet fejl. Prøv venligst igen." + "Venter på din anden enhed" diff --git a/features/linknewdevice/impl/src/main/res/values-de/translations.xml b/features/linknewdevice/impl/src/main/res/values-de/translations.xml index b8ad8b80ef..773878c736 100644 --- a/features/linknewdevice/impl/src/main/res/values-de/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-de/translations.xml @@ -34,6 +34,8 @@ "Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN." "Wenn das nicht funktioniert, melde dich manuell an" "Die Verbindung ist nicht sicher" + "Du wirst aufgefordert, die beiden unten abgebildeten Ziffern einzugeben." + "Trage die unten angezeigte Zahl auf einem anderen Device ein" "Die Anmeldung wurde auf dem anderen Gerät abgebrochen." "Anmeldeanfrage abgebrochen" "Die Anmeldung auf dem anderen Gerät wurde abgelehnt." @@ -54,4 +56,5 @@ Versuche, dich manuell anzumelden, oder scanne den QR-Code mit einem anderen Ger "Du musst %1$s die Berechtigung erteilen, die Kamera deines Geräts zu verwenden, um fortzufahren." "Erlaube Zugriff auf die Kamera zum Scannen des QR-Codes" "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut." + "Warten auf dein anderes Gerät" diff --git a/features/linknewdevice/impl/src/main/res/values-el/translations.xml b/features/linknewdevice/impl/src/main/res/values-el/translations.xml index 26c917075b..6c0e77da40 100644 --- a/features/linknewdevice/impl/src/main/res/values-el/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-el/translations.xml @@ -34,6 +34,8 @@ "Εάν αντιμετωπίσεις το ίδιο πρόβλημα, δοκίμασε ένα διαφορετικό δίκτυο wifi ή χρησιμοποίησε τα δεδομένα του κινητού σου αντί για wifi" "Εάν δεν λειτουργήσει, συνδέσου χειροκίνητα" "Η σύνδεση δεν είναι ασφαλής" + "Θα σου ζητηθεί να εισάγεις τα δύο ψηφία που εμφανίζονται σε αυτήν τη συσκευή." + "Εισήγαγε τον παρακάτω αριθμό στην άλλη συσκευή σου" "Η σύνδεση ακυρώθηκε στην άλλη συσκευή." "Το αίτημα σύνδεσης ακυρώθηκε" "Η σύνδεση απορρίφθηκε στην άλλη συσκευή." @@ -54,4 +56,5 @@ "Πρέπει να δώσεις άδεια για %1$s για να χρησιμοποιήσεις την κάμερα της συσκευής σου και να συνεχίσεις." "Επέτρεψε την πρόσβαση της κάμερας για σάρωση του κωδικού QR" "Παρουσιάστηκε ένα απροσδόκητο σφάλμα. Παρακαλώ προσπάθησε ξανά." + "Αναμονή για την άλλη σου συσκευή" diff --git a/features/linknewdevice/impl/src/main/res/values-es/translations.xml b/features/linknewdevice/impl/src/main/res/values-es/translations.xml index 032813a2c4..c33dc3ad88 100644 --- a/features/linknewdevice/impl/src/main/res/values-es/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-es/translations.xml @@ -17,6 +17,8 @@ "Si te encuentras con el mismo problema, prueba con una red wifi diferente o usa tus datos móviles en lugar de wifi" "Si eso no funciona, inicia sesión manualmente" "La conexión no es segura" + "Se te pedirá que introduzcas los dos dígitos mostrados en este dispositivo." + "Introduce el número que aparece a continuación en tu otro dispositivo" "El inicio de sesión se canceló en el otro dispositivo." "Solicitud de inicio de sesión cancelada" "El inicio de sesión se rechazó en el otro dispositivo." @@ -35,4 +37,5 @@ Intenta iniciar sesión manualmente o escanea el código QR con otro dispositivo "Tienes que dar permiso a %1$s para que utilice la cámara de tu dispositivo y así poder continuar." "Permite el acceso a la cámara para escanear el código QR" "Se ha producido un error inesperado. Vuelve a intentarlo." + "A la espera de tu otro dispositivo" diff --git a/features/linknewdevice/impl/src/main/res/values-et/translations.xml b/features/linknewdevice/impl/src/main/res/values-et/translations.xml index 6aa1398e0a..10f8af5e11 100644 --- a/features/linknewdevice/impl/src/main/res/values-et/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-et/translations.xml @@ -34,6 +34,8 @@ "Kui sama probleem kordub, siis kasuta mõnda muud WiFi- või mobiilset andmedsideühendust" "Kui see ka ei aita, siis logi sisse käsitsi" "Ühendus pole turvaline" + "Sul palutakse sisestada kaks selles seadmes kuvatud numbrit." + "Sisesta see number oma teises seadmes" "Sisselogimine katkestati teises seadmes." "Sisselogimispäring on tühistatud" "Sisselogimisest on teises seadmes keeldutud." @@ -54,4 +56,5 @@ Proovi käsitsi sisselogimist või skaneeri QR-koodi mõne muu seadmega.""Jätkamiseks pead lubama, et %1$s saab kasutada sinu nutiseadme kaamerat" "QR-koodi lugemiseks luba kaamerat kasutada" "Tekkis ootamatu viga. Palun proovi uuesti." + "Ootame sinu teise seadme järgi" diff --git a/features/linknewdevice/impl/src/main/res/values-eu/translations.xml b/features/linknewdevice/impl/src/main/res/values-eu/translations.xml index 06cc0fd857..8680ad94c7 100644 --- a/features/linknewdevice/impl/src/main/res/values-eu/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-eu/translations.xml @@ -16,6 +16,8 @@ "Saiatu berriro QR kodearekin saioa hasten sare-arazo bat izan bada" "Horrek ez badu funtzionatzen, hasi saioa eskuz" "Konexioa ez da segurua" + "Gailu honetan agertzen diren bi digituak sartzeko eskatuko zaizu." + "Sartu beheko zenbakia beste gailuan" "Saioa hasteko eskaera bertan behera utzi da beste gailuan" "Saioa hasteko eskaera bertan behera utzi da" "Saioa hasteari uko egin zaio beste dispositiboan." @@ -33,4 +35,5 @@ Saiatu saioa eskuz hasten, edo eskaneatu QR kodea beste gailu batean." "QR kode okerra" "Baimendu kameraren sarbidea QR kodea eskaneatzeko" "Ustekabeko errore bat gertatu da. Saiatu berriro." + "Beste gailuaren zain" diff --git a/features/linknewdevice/impl/src/main/res/values-fa/translations.xml b/features/linknewdevice/impl/src/main/res/values-fa/translations.xml index 804fa653ad..07c329ef6a 100644 --- a/features/linknewdevice/impl/src/main/res/values-fa/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fa/translations.xml @@ -15,6 +15,8 @@ "اکنون چه؟" "ورود دستی در صورت کار نکردنش" "اتّصال ناامن" + "از شما خواسته خواهد شد که دو رقم نشان داده روی این افزاره را وارد کنید." + "شمارهٔ زیر را روی افزارهٔ دیگرتان وارد کنید" "ورود روی افزارهٔ دیگر لغو شد." "درخواست ورد لغو شد" "ورود به دست افزارهٔ دیگر رد شد." @@ -33,4 +35,5 @@ "برای ادامه باید اجازهٔ استفادهٔ %1$s از دوربین افزاره‌تان را بدهید." "اجازهٔ دسترسی دوربین برای پویش کد پاس" "خطایی غیرمنتظره رخ داد. لطفاً دوباره تلاش کنید." + "منتظر افزارهٔ دیگرتان" diff --git a/features/linknewdevice/impl/src/main/res/values-fi/translations.xml b/features/linknewdevice/impl/src/main/res/values-fi/translations.xml index f8e999f886..0ba5a30e58 100644 --- a/features/linknewdevice/impl/src/main/res/values-fi/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fi/translations.xml @@ -34,6 +34,8 @@ "Jos kohtaat saman ongelman, kokeile toista wifi-verkkoa tai käytä mobiilidataa wifi-yhteyden sijaan" "Jos tämä ei auta, kirjaudu sisään manuaalisesti" "Yhteys ei ole turvallinen" + "Sinua pyydetään antamaan tässä laitteessa näkyvät kaksi numeroa." + "Kirjoita alla oleva numero toisella laitteellasi" "Kirjautuminen peruutettiin toisella laitteella." "Kirjautumispyyntö peruutettu" "Kirjautuminen hylättiin toisella laitteella." @@ -54,4 +56,5 @@ Yritä kirjautua sisään manuaalisesti tai skannaa QR-koodi toisella laitteella "Jatkaaksesi sinun on annettava lupa %1$s -sovellukselle käyttää laitteesi kameraa." "Salli lupa kameraan QR-koodin skannaamiseksi" "Tapahtui odottamaton virhe. Yritä uudelleen." + "Odotetaan toista laitettasi" diff --git a/features/linknewdevice/impl/src/main/res/values-fr/translations.xml b/features/linknewdevice/impl/src/main/res/values-fr/translations.xml index 0c91dca7a1..12a770af17 100644 --- a/features/linknewdevice/impl/src/main/res/values-fr/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-fr/translations.xml @@ -34,6 +34,8 @@ "Si vous rencontrez le même problème, essayez un autre réseau wifi ou utilisez vos données mobiles au lieu du wifi" "Si cela ne fonctionne pas, connectez-vous manuellement" "La connexion n’est pas sécurisée" + "Il vous sera demandé de saisir les deux chiffres affichés sur cet appareil." + "Saisissez le nombre ci-dessous sur votre autre appareil" "La connexion a été annulée sur l’autre appareil." "Demande de connexion annulée" "La connexion a été refusée sur l’autre appareil." @@ -52,4 +54,5 @@ "Vous devez autoriser %1$s à utiliser la camera de votre appareil pour continuer." "Autoriser l’usage de la caméra pour scanner le code QR" "Une erreur inattendue s’est produite. Veuillez réessayer." + "En attente de votre autre session" diff --git a/features/linknewdevice/impl/src/main/res/values-hr/translations.xml b/features/linknewdevice/impl/src/main/res/values-hr/translations.xml index 20c194ef93..8561e63ad3 100644 --- a/features/linknewdevice/impl/src/main/res/values-hr/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-hr/translations.xml @@ -34,6 +34,8 @@ "Ako se problem ponovi, pokušajte s drugom Wi-Fi mrežom ili mobilnim podatcima umjesto Wi-Fi-ja." "Ako to ne uspije, prijavite se ručno" "Veza nije sigurna" + "Od vas će se zatražiti da unesete dvije znamenke prikazane na ovom uređaju." + "Unesite ispod navedeni broj u svoj drugi uređaj" "Prijava je otkazana na drugom uređaju." "Zahtjev za prijavu je otkazan" "Prijava je odbijena na drugom uređaju." @@ -54,4 +56,5 @@ Pokušajte se prijaviti ručno ili skenirajte QR kod drugim uređajem." "Za nastavak morate dati dopuštenje za %1$s da biste se mogli služiti kamerom svog uređaja." "Dopustite pristup kameri kako biste mogli skenirati QR kod" "Došlo je do neočekivane pogreške. Pokušajte ponovno." + "Čekanje na vaš drugi uređaj" diff --git a/features/linknewdevice/impl/src/main/res/values-hu/translations.xml b/features/linknewdevice/impl/src/main/res/values-hu/translations.xml index 51fe30bbd8..8cefed94cc 100644 --- a/features/linknewdevice/impl/src/main/res/values-hu/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-hu/translations.xml @@ -34,6 +34,8 @@ "Ha ugyanezzel a problémával találkozik, próbálkozzon másik Wi-Fi-hálózattal, vagy a Wi-Fi helyett használja a mobil-adatkapcsolatát" "Ha ez nem működik, jelentkezzen be kézileg" "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" "A bejelentkezést megszakították a másik eszközön." "Bejelentkezési kérés törölve" "A bejelentkezést elutasították a másik eszközön." @@ -54,4 +56,5 @@ Próbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik e "A folytatáshoz engedélyeznie kell, hogy az %1$s használhassa az eszköz kameráját." "Engedélyezze a kamera elérését a QR-kód beolvasásához" "Váratlan hiba történt. Próbálja meg újra." + "Várakozás a másik eszközre" diff --git a/features/linknewdevice/impl/src/main/res/values-in/translations.xml b/features/linknewdevice/impl/src/main/res/values-in/translations.xml index 20badba9ba..5508993090 100644 --- a/features/linknewdevice/impl/src/main/res/values-in/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-in/translations.xml @@ -17,6 +17,8 @@ "Jika Anda mengalami masalah yang sama, coba jaringan Wi-Fi yang berbeda atau gunakan data seluler Anda daripada Wi-Fi" "Jika tidak berhasil, masuk secara manual" "Koneksi tidak aman" + "Anda akan diminta untuk memasukkan dua digit yang ditunjukkan di perangkat ini." + "Masukkan nomor bawah di perangkat Anda yang lain" "Proses masuk dibatalkan di perangkat lain." "Permintaan masuk dibatalkan" "Proses masuk ditolak di perangkat lain." @@ -35,4 +37,5 @@ Coba masuk secara manual, atau pindai kode QR dengan perangkat lain." "Anda perlu memberikan izin ke %1$s untuk menggunakan kamera perangkat Anda untuk melanjutkan." "Izinkan akses kamera untuk memindai kode QR" "Terjadi kesalahan tak terduga. Silakan coba lagi." + "Menunggu perangkat Anda yang lain" diff --git a/features/linknewdevice/impl/src/main/res/values-it/translations.xml b/features/linknewdevice/impl/src/main/res/values-it/translations.xml index 9a6476823a..6a32c70fe4 100644 --- a/features/linknewdevice/impl/src/main/res/values-it/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-it/translations.xml @@ -34,6 +34,8 @@ "Se riscontri lo stesso problema, prova con un altra rete wifi o usa i dati mobili al posto del wifi." "Se il problema persiste, accedi manualmente" "La connessione non è sicura" + "Ti verrà chiesto di inserire le due cifre mostrate su questo dispositivo." + "Inserisci il numero qui sotto sull\'altro dispositivo" "L\'accesso è stato annullato sull\'altro dispositivo." "Richiesta di accesso annullata" "L\'accesso è stato rifiutato sull\'altro dispositivo." @@ -54,4 +56,5 @@ Prova ad accedere manualmente o scansiona il codice QR con un altro dispositivo. "Per continuare, è necessario fornire l\'autorizzazione a %1$s per utilizzare la fotocamera del dispositivo." "Consenti l\'accesso alla fotocamera per la scansione del codice QR" "Si è verificato un errore inatteso. Riprova." + "In attesa dell\'altro dispositivo" diff --git a/features/linknewdevice/impl/src/main/res/values-ja/translations.xml b/features/linknewdevice/impl/src/main/res/values-ja/translations.xml index 6cfd5baf84..c4f09dcd0e 100644 --- a/features/linknewdevice/impl/src/main/res/values-ja/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ja/translations.xml @@ -18,7 +18,7 @@ "サインインが無効です。もう一度試してください。" "サインインが時間内に完了しませんでした" "%1$s を他の端末で開いてください" - "%1$s を選択してください" + "%1$s を選択" "\"QRコードでサインイン\"" "表示されているQRコードを一方の端末で読み取ってください" "%1$s を他の端末で開いてください" @@ -34,6 +34,8 @@ "同様の問題が発生する場合は、異なるWi-Fiやモバイルデータ通信を試してください" "問題が解決しない場合は、手動でサインインしてください" "接続が安全ではありません" + "この端末に表示される2つの数字の入力を要求されます" + "もう一方に表示される数字を入力してください" "もう一方の端末がサインインをキャンセルしました" "サインインのリクエストがキャンセルされました" "もう一方の端末でサインインを拒否されました" @@ -54,4 +56,5 @@ "続行するには、%1$s にカメラの使用を許可する必要があります。" "QRコードを読み取るため、カメラへのアクセスを許可" "予期せぬ問題が発生しました。もう一度試してください。" + "一方の端末を待機しています" diff --git a/features/linknewdevice/impl/src/main/res/values-ko/translations.xml b/features/linknewdevice/impl/src/main/res/values-ko/translations.xml index 3b31c8fdc2..1d69f58ca5 100644 --- a/features/linknewdevice/impl/src/main/res/values-ko/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ko/translations.xml @@ -34,6 +34,8 @@ "동일한 문제를 겪으신 경우 다른 Wi-Fi 네트워크를 사용해 보거나 Wi-Fi 대신 모바일 데이터를 사용해 보세요." "만약 작동하지 않는 경우, 수동으로 로그인하세요." "연결이 안전하지 않습니다" + "이 장치에 표시된 두 자리 숫자를 입력하라는 메시지가 표시됩니다." + "다른 device 에 아래 번호를 입력하세요" "다른 기기에서 로그인이 취소되었습니다." "로그인 요청이 취소되었습니다" "다른 기기에서 로그인이 거부되었습니다." @@ -54,4 +56,5 @@ "계속하려면 %1$s 가 기기의 카메라를 사용할 수 있도록 권한을 부여해야 합니다." "카메라 액세스를 허용하여 QR 코드를 스캔하세요" "예기치 않은 오류가 발생했습니다. 다시 시도해 주세요." + "다른 기기를 기다리고 있습니다" diff --git a/features/linknewdevice/impl/src/main/res/values-nb/translations.xml b/features/linknewdevice/impl/src/main/res/values-nb/translations.xml index 6b8541b25b..4e8844ee2f 100644 --- a/features/linknewdevice/impl/src/main/res/values-nb/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-nb/translations.xml @@ -34,6 +34,8 @@ "Hvis du støter på det samme problemet, kan du prøve et annet wifi-nettverk eller bruke mobildata i stedet for wifi" "Hvis det ikke fungerer, kan du logge på manuelt" "Forbindelsen er ikke sikker" + "Du blir bedt om å skrive inn de to sifrene som vises på denne enheten." + "Skriv inn nummeret nedenfor på den andre enheten" "Påloggingen ble kansellert på den andre enheten." "Påloggingsforespørsel kansellert" "Påloggingen ble avvist på den andre enheten." @@ -54,4 +56,5 @@ Prøv å logge på manuelt, eller skann QR-koden med en annen enhet." "Du må gi tillatelse til at %1$s kan bruke enhetens kamera for å fortsette." "Tillat kameratilgang for å skanne QR-koden" "Det oppstod en uventet feil. Prøv igjen." + "Venter på den andre enheten din" diff --git a/features/linknewdevice/impl/src/main/res/values-nl/translations.xml b/features/linknewdevice/impl/src/main/res/values-nl/translations.xml index 407a470e48..6dbc2c18c2 100644 --- a/features/linknewdevice/impl/src/main/res/values-nl/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-nl/translations.xml @@ -17,6 +17,8 @@ "Als je hetzelfde probleem ondervindt, probeer dan een ander wifi-netwerk of gebruik je mobiele data in plaats van wifi." "Als dat niet werkt, log dan handmatig in" "Verbinding niet veilig" + "Daar word je gevraagd om de twee cijfers in te voeren die op dit apparaat worden weergegeven." + "Voer het onderstaande nummer in op je andere apparaat" "De aanmelding is geannuleerd op het andere apparaat." "Login verzoek geannuleerd" "De aanmelding is geweigerd op het andere apparaat." @@ -35,4 +37,5 @@ Probeer handmatig in te loggen, of scan de QR code met een ander apparaat.""Je moet %1$s toestemming geven om de camera van je apparaat te gebruiken om verder te gaan." "Cameratoegang toestaan om de QR-code te scannen" "Er is een onverwachte fout opgetreden. Probeer het opnieuw." + "Aan het wachten op je andere apparaat" diff --git a/features/linknewdevice/impl/src/main/res/values-pl/translations.xml b/features/linknewdevice/impl/src/main/res/values-pl/translations.xml index 4db42a2a49..18b731a528 100644 --- a/features/linknewdevice/impl/src/main/res/values-pl/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-pl/translations.xml @@ -17,6 +17,8 @@ "Jeśli napotkasz ten sam problem, użyj innej sieci Wi-FI lub danych mobilnych" "Jeśli to nie zadziała, zaloguj się ręcznie" "Połączenie nie jest bezpieczne" + "Zostaniesz poproszony o wprowadzenie dwóch cyfr widocznych na tym urządzeniu." + "Wprowadź numer poniżej na innym urządzeniu" "Logowanie zostało anulowane na drugim urządzeniu." "Prośba o logowanie została anulowana" "Logowanie zostało odrzucone na drugim urządzeniu." @@ -35,4 +37,5 @@ Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu.""Musisz przyznać uprawnienia %1$s do korzystania z kamery, aby kontynuować." "Zezwól na dostęp do kamery, aby zeskanować kod QR" "Wystąpił nieoczekiwany błąd. Spróbuj ponownie." + "Oczekiwanie na drugie urządzenie" diff --git a/features/linknewdevice/impl/src/main/res/values-pt-rBR/translations.xml b/features/linknewdevice/impl/src/main/res/values-pt-rBR/translations.xml index f11bdc6e6d..1680e5c0ff 100644 --- a/features/linknewdevice/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-pt-rBR/translations.xml @@ -34,6 +34,8 @@ "Se o problema persistir, tente uma rede Wi-Fi diferente ou use seus dados móveis em vez de Wi-Fi" "Se isso não funcionar, entre manualmente" "Conexão insegura" + "Você será solicitado a inserir os dois dígitos mostrados neste dispositivo." + "Digite o número abaixo no seu outro dispositivo" "A entrada foi cancelada no outro dispositivo." "Solicitação de entrada foi cancelada" "A entrada foi recusada no outro dispositivo." @@ -54,4 +56,5 @@ Tente entrar manualmente ou ler o código QR com outro dispositivo." "Você deve permitir que o %1$s use a câmera do seu dispositivo para continuar." "Permita o acesso à câmera para ler o código QR" "Ocorreu um erro inesperado. Tente novamente." + "Aguardando seu outro dispositivo" diff --git a/features/linknewdevice/impl/src/main/res/values-pt/translations.xml b/features/linknewdevice/impl/src/main/res/values-pt/translations.xml index da6da08f38..eff27b17f0 100644 --- a/features/linknewdevice/impl/src/main/res/values-pt/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-pt/translations.xml @@ -17,6 +17,8 @@ "Se tiveres o mesmo problema, experimenta uma rede Wi-Fi diferente ou utiliza os teus dados móveis." "Se isso não funcionar, inicia sessão manualmente" "Ligação insegura" + "Ser-te-á pedido que insiras os dois dígitos indicados neste dispositivo." + "Insere o número abaixo no teu dispositivo" "O início de sessão foi cancelado no outro dispositivo." "Pedido de início de sessão cancelado" "O início de sessão foi rejeitado no outro dispositivo." @@ -35,4 +37,5 @@ Tenta iniciar a sessão manualmente ou digitaliza o código QR com outro disposi "Para continuar, tens que dar permissão à %1$s para aceder à câmara do teu dispositivo." "Permitir o acesso à câmara para ler o código QR" "Ocorreu um erro inesperado. Tenta novamente." + "À espera do teu outro dispositivo" diff --git a/features/linknewdevice/impl/src/main/res/values-ro/translations.xml b/features/linknewdevice/impl/src/main/res/values-ro/translations.xml index f1a4f3db59..c99b537084 100644 --- a/features/linknewdevice/impl/src/main/res/values-ro/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ro/translations.xml @@ -33,6 +33,8 @@ "Dacă întâmpinați aceeași problemă, încercați o altă rețea Wi-Fi sau utilizați datele mobile în loc de Wi-Fi." "Dacă nu funcționează, conectați-vă manual" "Conexiunea nu este sigură" + "Vi se va cere să introduceți cele două cifre afișate pe acest dispozitiv." + "Introduceți numărul de mai jos pe celălalt dispozitiv" "Autentificarea a fost anulată de pe celălalt dispozitiv." "Cererea de autentificare a fost anulată" "Autentificarea a fost refuzată pe celălalt dispozitiv." @@ -51,4 +53,5 @@ "Trebuie să acordați permisiunea ca %1$s să folosească camera dispozitivului pentru a continua." "Permiteți accesul la cameră pentru a scana codul QR" "A apărut o eroare neașteptată. Vă rugăm să încercați din nou." + "În așteptarea celuilalt dispozitiv" diff --git a/features/linknewdevice/impl/src/main/res/values-ru/translations.xml b/features/linknewdevice/impl/src/main/res/values-ru/translations.xml index 39506417b6..6a8b645c4f 100644 --- a/features/linknewdevice/impl/src/main/res/values-ru/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ru/translations.xml @@ -34,6 +34,8 @@ "Если вы столкнулись с той же проблемой, попробуйте сменить точку доступа Wi-Fi или используйте мобильные данные" "Если это не помогло, войдите вручную" "Соединение не защищено" + "Вам нужно будет ввести две цифры, показанные на этом устройстве." + "Введите показанный номер на своем другом устройстве" "Вход на другом устройстве был отменен." "Запрос на вход отменен" "Вход в систему был отклонен на другом устройстве." @@ -54,4 +56,5 @@ "Чтобы продолжить, вам необходимо разрешить %1$s использовать камеру вашего устройства." "Разрешите доступ к камере для сканирования QR-кода" "Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз." + "Ожидание другого устройства" diff --git a/features/linknewdevice/impl/src/main/res/values-sk/translations.xml b/features/linknewdevice/impl/src/main/res/values-sk/translations.xml index cb430671bb..f64c01328b 100644 --- a/features/linknewdevice/impl/src/main/res/values-sk/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-sk/translations.xml @@ -34,6 +34,8 @@ "Ak narazíte na rovnaký problém, vyskúšajte inú sieť Wi-Fi alebo namiesto siete Wi-Fi použite mobilné dáta" "Ak to nefunguje, prihláste sa manuálne" "Pripojenie nie je bezpečené" + "Budete požiadaní o zadanie dvoch číslic zobrazených na tomto zariadení." + "Zadajte nižšie uvedené číslo na vašom druhom zariadení" "Prihlásenie bolo zrušené na druhom zariadení." "Žiadosť o prihlásenie bola zrušená" "Prihlásenie bolo zamietnuté na druhom zariadení." @@ -54,4 +56,5 @@ Skúste sa prihlásiť manuálne alebo naskenujte QR kód pomocou iného zariade "Ak chcete pokračovať, musíte udeliť povolenie aplikácii %1$s používať fotoaparát vášho zariadenia." "Povoľte prístup k fotoaparátu na naskenovanie QR kódu" "Vyskytla sa neočakávaná chyba. Prosím, skúste to znova." + "Čaká sa na vaše druhé zariadenie" diff --git a/features/linknewdevice/impl/src/main/res/values-sv/translations.xml b/features/linknewdevice/impl/src/main/res/values-sv/translations.xml index 8a1bef434a..8800b31bbd 100644 --- a/features/linknewdevice/impl/src/main/res/values-sv/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-sv/translations.xml @@ -17,6 +17,8 @@ "Om du stöter på samma problem, prova ett annat wifi-nätverk eller använd din mobildata istället för wifi" "Om det inte fungerar, logga in manuellt" "Anslutningen är inte säker" + "Du kommer att bli ombedd att ange de två siffrorna som visas på den här enheten." + "Ange numret nedan på din andra enhet" "Inloggningen avbröts på den andra enheten." "Inloggningsförfrågan avbröts" "Inloggningen avvisades på den andra enheten." @@ -35,4 +37,5 @@ Prova att logga in manuellt eller skanna QR-koden med en annan enhet." "Du måste ge tillstånd för %1$s att använda enhetens kamera för att kunna fortsätta." "Tillåt kameraåtkomst för att skanna QR-koden" "Ett oväntat fel inträffade. Vänligen försök igen." + "Väntar på din andra enhet" diff --git a/features/linknewdevice/impl/src/main/res/values-tr/translations.xml b/features/linknewdevice/impl/src/main/res/values-tr/translations.xml index e5fd989c0b..3c8767f64c 100644 --- a/features/linknewdevice/impl/src/main/res/values-tr/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-tr/translations.xml @@ -33,6 +33,8 @@ "Aynı sorunla karşılaşırsanız, farklı bir wifi ağı deneyin veya wifi yerine mobil verinizi kullanın" "Bu işe yaramazsa, manuel olarak oturum açın" "Bağlantı güvenli değil" + "Bu cihazda gösterilen iki haneyi girmeniz istenecektir." + "Aşağıdaki numarayı diğer cihazınıza girin" "Oturum açma işlemi diğer cihazda iptal edildi." "Oturum açma isteği iptal edildi" "Diğer cihazda oturum açma işlemi reddedildi." @@ -51,4 +53,5 @@ Manuel olarak oturum açmayı deneyin veya QR kodunu başka bir cihazla tarayın "Devam etmek için %1$s cihazınızın kamerasını kullanmasına izin vermeniz gerekir." "QR kodunu taramak için kamera erişimine izin verin" "Beklenmeyen bir hata oluştu. Lütfen tekrar deneyin." + "Diğer cihazınız bekleniyor" diff --git a/features/linknewdevice/impl/src/main/res/values-uk/translations.xml b/features/linknewdevice/impl/src/main/res/values-uk/translations.xml index 875b5aed16..fe5b1a7460 100644 --- a/features/linknewdevice/impl/src/main/res/values-uk/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-uk/translations.xml @@ -33,6 +33,8 @@ "Якщо ви зіткнулися з тією ж проблемою, спробуйте іншу мережу Wi-Fi або використовуйте мобільний інтернет замість Wi-Fi" "Якщо це не спрацює, увійдіть вручну" "З\'єднання не безпечне" + "Вас попросять ввести дві цифри, показані на цьому пристрої." + "Введіть номер нижче на іншому пристрої" "Вхід було скасовано на іншому пристрої." "Запит на вхід скасовано" "Вхід був відхилений на іншому пристрої." @@ -53,4 +55,5 @@ "Вам потрібно дати дозвіл %1$s на використання камери вашого пристрою, щоб продовжити." "Надайте доступ до камери, щоб сканувати QR-код" "Сталася несподівана помилка. Будь ласка, спробуйте ще раз." + "Чекаємо на ваш інший пристрій" diff --git a/features/linknewdevice/impl/src/main/res/values-ur/translations.xml b/features/linknewdevice/impl/src/main/res/values-ur/translations.xml index 54d2c2e401..0b4bb02226 100644 --- a/features/linknewdevice/impl/src/main/res/values-ur/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ur/translations.xml @@ -17,6 +17,8 @@ "اگر آپ کو بھی یہی مسئلہ درپیش ہو، تو کوئی دوسرا وائی فائی شبکہ آزمائیں یا وائی فائی کے بجائے اپنے محمول بیانات استعمال کریں۔" "اگر یہ کام نہ کرے، تو دستی طور پر داخل ہوں" "اتصال محفوظ نہیں" + "آپ سے اس آلے پر دکھائے گئے دو ہندسوں کو درج کرنے کو کہا جائے گا۔" + "اپنے دوسرے آلے پر درج ذیل نمبر درج کریں" "دوسرے آلے پر دخول منسوخ کر دیا گیا تھا۔" "دخول کی درخواست منسوخ" "دوسرے آلہ پر دخول کو مسترد کر دیا گیا تھا۔" @@ -35,4 +37,5 @@ "جاری رکھنے کے لیے آپ %1$s کو اپنے آلے کا تصویرگر استعمال کرنے کی اجازت دینے کی ضرورت ہے۔" "کیو آر رمز کو مسح ضوئی کرنے کے لئے تصویرگر تک رسائی کی اجازت دیں" "ایک غیر متوقع نقص واقع ہوا۔ برائے مہربانی دوبارہ کوشش کریں۔" + "آپکے دوسرے آلے کا منتظر" diff --git a/features/linknewdevice/impl/src/main/res/values-uz/translations.xml b/features/linknewdevice/impl/src/main/res/values-uz/translations.xml index ed08ee1a04..1d436b5f4a 100644 --- a/features/linknewdevice/impl/src/main/res/values-uz/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-uz/translations.xml @@ -34,6 +34,8 @@ "Xuddi shu muammoga duch kelsangiz, boshqa wifi tarmogʻini sinang yoki wifi oʻrniga mobil internetdan foydalaning" "Agar bunisi ishlamasa, oddiy usulda kiring" "Ulanish xavfsiz emas" + "Sizdan ushbu qurilmada koʻrsatilgan ikkita raqamni kiritish soʻraladi." + "Narigi qurilmada quyidagi raqamni kiriting" "Boshqa qurilmadan hisobga kirish bekor qilindi." "Tizimga kirish soʻrovi bekor qilindi" "Boshqa qurilmadan hisobga kirish bekor qilindi." @@ -54,4 +56,5 @@ Oddiy usulda kiring yoki boshqa qurilma bilan QR kodni skanerlang." "Davom etish uchun %1$s qurilmangiz kamerasidan foydalanishiga ruxsat berishingiz kerak." "QR kodni skanerlash uchun kameraga ruxsat bering" "Kutilmagan xatolik yuz berdi. Qayta urining." + "Boshqa qurilmangiz kutilmoqda" diff --git a/features/linknewdevice/impl/src/main/res/values-zh-rTW/translations.xml b/features/linknewdevice/impl/src/main/res/values-zh-rTW/translations.xml index 3aa047125a..e18fbc13b6 100644 --- a/features/linknewdevice/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-zh-rTW/translations.xml @@ -34,6 +34,8 @@ "如果遇到相同的問題,請嘗試使用其他 wifi 網路或您的行動數據" "若無法運作,請手動登入" "連線不安全" + "系統會要求您輸入此裝置上顯示的兩位數字。" + "在您的其他裝置上輸入以下數字" "已在其他裝置上取消登入。" "已取消登入請求" "其他裝置拒絕登入。" @@ -54,4 +56,5 @@ "您必須授予 %1$s 權限以使用裝置相機才能繼續。" "允許相機權限以掃描 QR code" "發生意外錯誤。請再試一次。" + "等待您的其他裝置" diff --git a/features/linknewdevice/impl/src/main/res/values-zh/translations.xml b/features/linknewdevice/impl/src/main/res/values-zh/translations.xml index 1bd74d5104..cabcb5ccd0 100644 --- a/features/linknewdevice/impl/src/main/res/values-zh/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-zh/translations.xml @@ -34,6 +34,8 @@ "如果遇到同样的问题,请尝试使用不同的 WiFi 网络或使用移动数据代替 WiFi" "如果不起作用,请手动登录" "连接不安全" + "你将被要求输入此设备上显示的两位数字。" + "在你的其它设备上输入以下数字" "登录被另一台设备取消" "登录请求已取消" "另一设备上的登录请求已被拒绝。" @@ -54,4 +56,5 @@ "你需要授予 %1$s 使用设备摄像头的权限才能继续。" "允许访问摄像头以扫描二维码" "发生了意外错误。请再试一次。" + "正在等待其它设备" diff --git a/features/location/impl/src/main/res/values-da/translations.xml b/features/location/impl/src/main/res/values-da/translations.xml index f15ab0fb2f..4d4c5d00bc 100644 --- a/features/location/impl/src/main/res/values-da/translations.xml +++ b/features/location/impl/src/main/res/values-da/translations.xml @@ -1,4 +1,5 @@ + "Din live-positionshistorik gemmes i rummet og er synlig for medlemmerne, når sessionen er afsluttet." "Vælg, hvor længe du vil dele din aktuelle position." diff --git a/features/lockscreen/impl/src/main/res/values-fa/translations.xml b/features/lockscreen/impl/src/main/res/values-fa/translations.xml index 56dc91e835..bd2000b94d 100644 --- a/features/lockscreen/impl/src/main/res/values-fa/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-fa/translations.xml @@ -34,5 +34,5 @@ "استفاده از زیست‌سنجی" "استفاده از پین" - "خارج شدن…" + "برداشتن افزاره…" diff --git a/features/lockscreen/impl/src/main/res/values-in/translations.xml b/features/lockscreen/impl/src/main/res/values-in/translations.xml index 0396f56b0c..e0054cda62 100644 --- a/features/lockscreen/impl/src/main/res/values-in/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-in/translations.xml @@ -32,5 +32,5 @@ Pilih sesuatu yang mudah untuk diingat. Jika Anda lupa PIN ini, Anda akan dikelu "Gunakan biometrik" "Gunakan PIN" - "Mengeluarkan dari akun…" + "Mengeluarkan device dari akun…" diff --git a/features/lockscreen/impl/src/main/res/values-pt/translations.xml b/features/lockscreen/impl/src/main/res/values-pt/translations.xml index a6b2516fba..fca6fbcb9e 100644 --- a/features/lockscreen/impl/src/main/res/values-pt/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-pt/translations.xml @@ -23,7 +23,7 @@ Escolhe algo memorável. Se te esqueceres deste PIN, a tua sessão será termina "Insere o mesmo PIN duas vezes" "Os PINs não coincidem" "Terás de voltar a iniciar sessão e criar um novo PIN para continuar" - "Estás a terminar a sessão" + "O teu dispositivo está a ser removido" "Tens %1$d tentativa de desbloqueio" "Tens %1$d tentativas de desbloqueio" @@ -34,5 +34,5 @@ Escolhe algo memorável. Se te esqueceres deste PIN, a tua sessão será termina "Utilizar biometria" "Utilizar PIN" - "A terminar sessão…" + "A remover dispositivo…" diff --git a/features/lockscreen/impl/src/main/res/values-zh/translations.xml b/features/lockscreen/impl/src/main/res/values-zh/translations.xml index 62da77cbb5..f3e93668fd 100644 --- a/features/lockscreen/impl/src/main/res/values-zh/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-zh/translations.xml @@ -32,5 +32,5 @@ "使用生物识别" "使用 PIN 码" - "正在删除设备……" + "正在移除设备…" diff --git a/features/login/impl/src/main/res/values-be/translations.xml b/features/login/impl/src/main/res/values-be/translations.xml index 307ccf7263..d9d69503c5 100644 --- a/features/login/impl/src/main/res/values-be/translations.xml +++ b/features/login/impl/src/main/res/values-be/translations.xml @@ -24,7 +24,7 @@ "Няправільнае імя карыстальніка і/або пароль" "Гэта несапраўдны ідэнтыфікатар карыстальніка. Чаканы фармат: ‘@user:homeserver.org’" "Гэты сервер настроены на выкарыстанне маркераў абнаўлення. Яны не падтрымліваюцца пры ўваходзе на аснове пароля." - "Выбраны хатні сервер не падтрымлівае пароль або ўваход у OIDC. Калі ласка, звярніцеся да адміністратара або абярыце іншы хатні сервер." + "Выбраны хатні сервер не падтрымлівае пароль або ўваход у OAuth. Калі ласка, звярніцеся да адміністратара або абярыце іншы хатні сервер." "Увядзіце свае даныя" "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі." "Сардэчна запрашаем!" diff --git a/features/login/impl/src/main/res/values-bg/translations.xml b/features/login/impl/src/main/res/values-bg/translations.xml index 6ccc7c9129..c6c74ff1a8 100644 --- a/features/login/impl/src/main/res/values-bg/translations.xml +++ b/features/login/impl/src/main/res/values-bg/translations.xml @@ -21,7 +21,7 @@ "Този акаунт бе деактивиран." "Неправилно потребителско име и/или парола" "Това не е валиден потребителски идентификатор. Очакван формат: ‘@user:homeserver.org’" - "Избраният сървър не поддържа влизане с парола или OIDC. Моля, свържете се с вашия администратор или изберете друг сървър." + "Избраният сървър не поддържа влизане с парола или OAuth. Моля, свържете се с вашия администратор или изберете друг сървър." "Въведете своите данни" "Matrix е отворена мрежа за сигурна, децентрализирана комуникация." "Добре дошли отново!" diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml index fa31796c3b..c5887de6c6 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -32,7 +32,7 @@ "Nesprávné uživatelské jméno nebo heslo" "Toto není platný identifikátor uživatele. Očekávaný formát: \'@user:homeserver.org\'" "Tento server je nakonfigurován tak, aby používal obnovovací tokeny. Ty nejsou podporovány při použití přihlašovacích údajů založených na hesle." - "Vybraný domovský server nepodporuje přihlášení pomocí hesla nebo OIDC. Kontaktujte prosím svého správce nebo vyberte jiný domovský server." + "Vybraný domovský server nepodporuje přihlášení pomocí hesla nebo OAuth. Kontaktujte prosím svého správce nebo vyberte jiný domovský server." "Zadejte své údaje" "Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci." "Vítejte zpět!" diff --git a/features/login/impl/src/main/res/values-cy/translations.xml b/features/login/impl/src/main/res/values-cy/translations.xml index b8988a9889..0f44287ed4 100644 --- a/features/login/impl/src/main/res/values-cy/translations.xml +++ b/features/login/impl/src/main/res/values-cy/translations.xml @@ -32,7 +32,7 @@ "Enw defnyddiwr a/neu gyfrinair anghywir" "Nid yw hwn yn ddynodwr defnyddiwr dilys. Fformat disgwyliedig: ‘@user:homeserver.org’" "Mae\'r gweinydd hwn wedi\'i ffurfweddu i ddefnyddio tocynnau adnewyddu. Nid yw\'r rhain yn cael eu cefnogi wrth ddefnyddio mewngofnodi ar sail cyfrinair." - "Nid yw\'r gweinydd cartref ddewiswyd yn cefnogi cyfrinair na mewngofnodi OIDC. Cysylltwch â\'ch gweinyddwr neu dewis gweinydd cartref arall." + "Nid yw\'r gweinydd cartref ddewiswyd yn cefnogi cyfrinair na mewngofnodi OAuth. Cysylltwch â\'ch gweinyddwr neu dewis gweinydd cartref arall." "Rhowch eich manylion" "Mae Matrix yn rhwydwaith agored ar gyfer cyfathrebu diogel, datganoledig." "Croeso nôl!" diff --git a/features/login/impl/src/main/res/values-da/translations.xml b/features/login/impl/src/main/res/values-da/translations.xml index 284284cc3e..35d66a6e69 100644 --- a/features/login/impl/src/main/res/values-da/translations.xml +++ b/features/login/impl/src/main/res/values-da/translations.xml @@ -28,16 +28,24 @@ "Hvad er adressen på din server?" "Vælg din server" "Opret konto" - "Denne konto er blevet deaktiveret." + "Denne konto er blevet slettet." "Forkert brugernavn og/eller adgangskode" "Dette er ikke en gyldig brugeridentifikation. Forventet format: \'@bruger:hjemmeserver.org\'" "Denne server er konfigureret til at bruge opdateringstokens. Disse understøttes ikke, når du bruger adgangskodebaseret login." - "Den valgte hjemmeserver understøtter ikke adgangskode eller OIDC-login. Kontakt venligst din administrator eller vælg en anden hjemmeserver." + "Den valgte hjemmeserver understøtter ikke adgangskode eller OAuth-login. Kontakt venligst din administrator eller vælg en anden hjemmeserver." "Indtast dine oplysninger" "Matrix er et åbent netværk for sikker, decentraliseret kommunikation." "Velkommen tilbage!" "Log ind på %1$s" + "Åbn Element Classic" + "Åbn Element Classic på din enhed" + "Gå til Indstillinger > Sikkerhed og privatliv" + "I Nøgleadministration skal du, under Kryptografi, vælge Gendannelse af krypterede meddelelser" + "Følg instruktionerne for at aktivere dit nøglelager" + "Gå tilbage til %1$s" + "Aktivér dit nøglelager, før du fortsætter til %1$s" "Version %1$s" + "Kontoen kontrolleres…" "Log ind manuelt" "Log ind på %1$s" "Log ind med QR-kode" diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index dced91a1c9..89e4412d0f 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -28,11 +28,11 @@ "Wie lautet die Adresse deines Servers?" "Wähle deinen Server aus" "Konto erstellen" - "Dieses Konto wurde deaktiviert." + "Dieses Konto wurde gelöscht." "Falscher Nutzername und/oder Passwort" "Dies ist keine gültige Nutzerkennung. Erwartetes Format: \'@nutzer:homeserver.org\'" "Dieser Server ist so konfiguriert, dass er Refresh-Tokens verwendet. Diese werden für die passwortbasierte Anmeldung nicht unterstützt." - "Der ausgewählte Homeserver unterstützt weder den Login per Passwort noch per OIDC. Bitte kontaktiere deinen Administrator oder wähle einen anderen Homeserver." + "Der ausgewählte Homeserver unterstützt weder den Login per Passwort noch per OAuth. Bitte kontaktiere deinen Administrator oder wähle einen anderen Homeserver." "Gib deine Daten ein" "Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation." "Willkommen zurück!" diff --git a/features/login/impl/src/main/res/values-el/translations.xml b/features/login/impl/src/main/res/values-el/translations.xml index c87027477d..85640698f3 100644 --- a/features/login/impl/src/main/res/values-el/translations.xml +++ b/features/login/impl/src/main/res/values-el/translations.xml @@ -32,7 +32,7 @@ "Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης" "Αυτό δεν είναι έγκυρο αναγνωριστικό χρήστη. Αναμενόμενη μορφή: \'@χρήστης:homeserver.org\'" "Αυτός ο διακομιστής έχει ρυθμιστεί ώστε να χρησιμοποιεί διακριτικά ανανέωσης. Αυτά δεν υποστηρίζονται όταν χρησιμοποιείς σύνδεση μέσω κωδικού πρόσβασης." - "Ο επιλεγμένος οικιακός διακομιστής δεν υποστηρίζει κωδικό πρόσβασης ή σύνδεση OIDC. Επικοινωνήστε με τον διαχειριστή σου ή επέλεξε άλλο οικιακό διακομιστή." + "Ο επιλεγμένος οικιακός διακομιστής δεν υποστηρίζει κωδικό πρόσβασης ή σύνδεση OAuth. Επικοινωνήστε με τον διαχειριστή σου ή επέλεξε άλλο οικιακό διακομιστή." "Εισήγαγε τα στοιχεία σου" "Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία." "Καλωσόρισες ξανά!" diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml index df6a06ab29..3b9ffbe2dd 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -29,7 +29,7 @@ "Usuario y/o contraseña incorrectos" "Este no es un id de usuario válido. Formato esperado: \'@user:homeserver.org\'" "Este servidor está configurado para utilizar tokens de actualización. Estos no son compatibles cuando se utiliza el inicio de sesión basado en contraseña." - "El servidor base seleccionado no admite el inicio de sesión usando contraseña ni OIDC. Ponte en contacto con tu administrador o elige otro servidor base." + "El servidor base seleccionado no admite el inicio de sesión usando contraseña ni OAuth. Ponte en contacto con tu administrador o elige otro servidor base." "Introduce tus datos" "Matrix es una red abierta para una comunicación segura y descentralizada." "¡Hola de nuevo!" diff --git a/features/login/impl/src/main/res/values-et/translations.xml b/features/login/impl/src/main/res/values-et/translations.xml index 7ac4e99694..b0b60009d4 100644 --- a/features/login/impl/src/main/res/values-et/translations.xml +++ b/features/login/impl/src/main/res/values-et/translations.xml @@ -32,7 +32,7 @@ "Vigane kasutajanimi ja/või salasõna" "See ei ole korrektne kasutajanimi. Õige vorming on: „@kasutaja:koduserver.ee“" "See server on seadistatud kasutama tunnusloa põhist sisselogimist. Salasõnaga sisselogimisel see võimalus aga ei ole toetatud." - "Valitud koduserver ei toeta salasõna ega OIDC-põhist sisselogimist. Lisateavet saad koduserveri haldajalt, aga sa võid ka valida mõne teise serveri." + "Valitud koduserver ei toeta salasõna ega OAuth-põhist sisselogimist. Lisateavet saad koduserveri haldajalt, aga sa võid ka valida mõne teise serveri." "Sisesta oma andmed" "Matrix on avatud võrk turvalise ja hajutatud suhtluse jaoks." "Tere tulemast tagasi!" diff --git a/features/login/impl/src/main/res/values-eu/translations.xml b/features/login/impl/src/main/res/values-eu/translations.xml index 355e63546c..78a13e5810 100644 --- a/features/login/impl/src/main/res/values-eu/translations.xml +++ b/features/login/impl/src/main/res/values-eu/translations.xml @@ -21,7 +21,7 @@ "Sortu kontua" "Kontu hau desaktibatuta dago." "Erabiltzaile-izena edo/eta pasahitza okerrak" - "Hautatutako zerbitzaria ez da bateragarria pasahitz edo OIDC saio-hasierarekin. Jarri harremanetan administratzailearekin edo aukeratu beste zerbitzari bat." + "Hautatutako zerbitzaria ez da bateragarria pasahitz edo OAuth saio-hasierarekin. Jarri harremanetan administratzailearekin edo aukeratu beste zerbitzari bat." "Sartu zure datuak" "Matrix komunikazio seguru eta deszentralizaturako sare irekia da." "Ongi etorri!" diff --git a/features/login/impl/src/main/res/values-fa/translations.xml b/features/login/impl/src/main/res/values-fa/translations.xml index ef7062a88a..d903103c1b 100644 --- a/features/login/impl/src/main/res/values-fa/translations.xml +++ b/features/login/impl/src/main/res/values-fa/translations.xml @@ -20,10 +20,10 @@ "نشانی کارسازتان چیست؟" "کارسازتان را برگزینید" "ایجاد حساب" - "این حساب از کار افتاده است." + "این حساب حذف شده است." "نام کاربری یا گذرواژه نامعتبر است" "این یک شناسه کاربری معتبر نیست. قالب صحیح: ‪«@user:homeserver.or" - "کارساز اصلی انتخاب شده از رمز عبور یا ورود OIDC پشتیبانی نمی کند. لطفا با مدیر خود تماس بگیرید یا یک کارساز خانگی دیگر را انتخاب کنید." + "کارساز اصلی انتخاب شده از رمز عبور یا ورود OAuth پشتیبانی نمی کند. لطفا با مدیر خود تماس بگیرید یا یک کارساز خانگی دیگر را انتخاب کنید." "جزییاتتان را وارد کنید" "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است." "خوش برگشتید!" diff --git a/features/login/impl/src/main/res/values-fi/translations.xml b/features/login/impl/src/main/res/values-fi/translations.xml index 0d795811b1..4f5225be67 100644 --- a/features/login/impl/src/main/res/values-fi/translations.xml +++ b/features/login/impl/src/main/res/values-fi/translations.xml @@ -32,7 +32,7 @@ "Väärä käyttäjänimi ja/tai salasana" "Tämä ei ole kelvollinen käyttäjätunnus. Odotettu muoto: \'@käyttäjä:kotipalvelin.fi\'" "Tämä palvelin on määritetty käyttämään refresh tokeneja. Näitä ei tueta salasanapohjaisen kirjautumisen kanssa." - "Valitsemasi kotipalvelin ei tue salasana- tai OIDC-kirjautumista. Ota yhteyttä palvelimesi ylläpitäjään tai valitse toinen kotipalvelin." + "Valitsemasi kotipalvelin ei tue salasana- tai OAuth-kirjautumista. Ota yhteyttä palvelimesi ylläpitäjään tai valitse toinen kotipalvelin." "Anna tietosi" "Matrix on avoin verkko turvallista, hajautettua viestintää varten." "Tervetuloa takaisin!" diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml index 3435eb7d40..504517aad7 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -28,11 +28,11 @@ "Quelle est l’adresse de votre serveur ?" "Choisissez votre serveur" "Créer un compte" - "Ce compte a été désactivé." + "Ce compte a été supprimé." "Nom d’utilisateur et/ou mot de passe incorrects" "Il ne s’agit pas d’un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »" "Ce serveur est configuré pour utiliser des tokens d’actualisation. Ils ne sont pas pris en charge lors de l’utilisation d’une connexion basée sur un mot de passe." - "Le serveur d’accueil sélectionné ne prend pas en charge le mot de passe ou la connexion OIDC. Contactez votre administrateur ou choisissez un autre serveur d’accueil." + "Le serveur d’accueil sélectionné ne prend pas en charge le mot de passe ou la connexion OAuth. Contactez votre administrateur ou choisissez un autre serveur d’accueil." "Saisissez vos identifiants" "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée." "Content de vous revoir !" diff --git a/features/login/impl/src/main/res/values-hr/translations.xml b/features/login/impl/src/main/res/values-hr/translations.xml index b52dd77d7f..f578790380 100644 --- a/features/login/impl/src/main/res/values-hr/translations.xml +++ b/features/login/impl/src/main/res/values-hr/translations.xml @@ -32,7 +32,7 @@ "Netočno korisničko ime i/ili zaporka" "To nije valjani identifikator korisnika. Očekivani oblik: ‘@korisnik:matičniposlužitelj.org’" "Ovaj je poslužitelj konfiguriran za korištenje tokena za osvježavanje. Oni nisu podržani kada se upotrebljava prijava temeljena na zaporki." - "Odabrani matični poslužitelj ne podržava zaporku ili OIDC prijavu. Obratite se administratoru ili odaberite drugi matični poslužitelj." + "Odabrani matični poslužitelj ne podržava zaporku ili OAuth prijavu. Obratite se administratoru ili odaberite drugi matični poslužitelj." "Unesite svoje podatke" "Matrix je otvorena mreža za sigurnu, decentraliziranu komunikaciju." "Dobro došli natrag!" 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 c2b2fefddb..26534cc523 100644 --- a/features/login/impl/src/main/res/values-hu/translations.xml +++ b/features/login/impl/src/main/res/values-hu/translations.xml @@ -28,11 +28,11 @@ "Mi a kiszolgálója címe?" "Válassza ki a kiszolgálóját" "Fiók létrehozása" - "Ez a fiók deaktiválva lett." + "Ez a fiók törölve lett." "Helytelen felhasználónév vagy jelszó" "Ez nem érvényes felhasználóazonosító. A várt formátum: „@user:homeserver.org”" "Ez a kiszolgáló frissítési tokenek használatára van beállítva. Ezek jelszó alapú bejelentkezés esetén nem támogatottak." - "A kiválasztott Matrix-kiszolgáló nem támogatja a jelszavas vagy OIDC-alapú bejelentkezést. Lépjen kapcsolatba a kiszolgáló adminisztrátorával, vagy válasszon másik Matrix-kiszolgálót." + "A kiválasztott Matrix-kiszolgáló nem támogatja a jelszavas vagy OAuth-alapú bejelentkezést. Lépjen kapcsolatba a kiszolgáló adminisztrátorával, vagy válasszon másik Matrix-kiszolgálót." "Adja meg adatait" "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz." "Örülünk, hogy visszatért!" 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 e05fd8746d..4b9f3ffdef 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -32,7 +32,7 @@ "Nama pengguna dan/atau kata sandi salah" "Ini bukan pengenal pengguna yang valid. Format yang diharapkan: \'@pengguna:homeserver.org\'" "Server ini diatur untuk menggunakan token penyegaran. Ini tidak didukung ketika menggunakan log masuk berbasis kata sandi." - "Homeserver yang dipilih tidak mendukung log masuk kata sandi atau OIDC. Silakan hubungi admin Anda atau pilih homeserver yang lain." + "Homeserver yang dipilih tidak mendukung log masuk kata sandi atau OAuth. Silakan hubungi admin Anda atau pilih homeserver yang lain." "Masukkan detail Anda" "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi." "Selamat datang kembali!" diff --git a/features/login/impl/src/main/res/values-it/translations.xml b/features/login/impl/src/main/res/values-it/translations.xml index e882654e4b..3753d58390 100644 --- a/features/login/impl/src/main/res/values-it/translations.xml +++ b/features/login/impl/src/main/res/values-it/translations.xml @@ -32,7 +32,7 @@ "Nome utente e/o password errati" "Questo non è un identità utente valida. il formato atteso é: \'@user:homeserver.org\'" "Questo server è configurato per usare i token di aggiornamento. Non sono supportati quando si usa l\'accesso basato su password." - "L\'homeserver selezionato non supporta la password o l\'accesso OIDC. Contatta il tuo amministratore o scegli un altro homeserver." + "L\'homeserver selezionato non supporta la password o l\'accesso OAuth. Contatta il tuo amministratore o scegli un altro homeserver." "Inserisci i tuoi dati" "Matrix è una rete aperta per comunicazioni sicure e decentralizzate." "Bentornato!" diff --git a/features/login/impl/src/main/res/values-ja/translations.xml b/features/login/impl/src/main/res/values-ja/translations.xml index 219605ab22..42698e27dc 100644 --- a/features/login/impl/src/main/res/values-ja/translations.xml +++ b/features/login/impl/src/main/res/values-ja/translations.xml @@ -1,18 +1,18 @@ - "アカウントの提供元を変更" + "アカウント提供元を変更" "ホームサーバーのアドレス" - "検索用のキーワードまたはドメインのアドレスを入力してください。" + "検索のキーワードまたはドメインのアドレスを入力してください。" "会社やコミュニティ, 個人のサーバーなどを検索します。" - "アカウントの提供元を検索" - "メールアプリのように、あなたの会話はここに保管されています。" + "アカウント提供元を検索" + "メールアプリのように、あなたの会話はこのサーバー上に保管されます。" "%s にサインインを試みています" - "メールアプリのように、あなたの会話はここに保管されています。" - "%s にアカウントの作成を試みています" + "メールアプリのように、あなたの会話はこのサーバー上に保管されます。" + "%s 上にアカウントを作成しようとしています" "Matrix.org は Matrix.org Foundation が運営する、大規模で安全な分散型コミュニケーションを実現する無償のサーバーです。" "その他" - "自身のサーバーや仕事用のアカウントにサインインするには、アカウント提供元のサーバーを指定してください。" - "アカウントの提供元を変更" + "自身のサーバーや仕事用のアカウントにサインインするには、アカウント提供元を変更してください。" + "アカウント提供元を変更" "Google Play" "%1$s では Element Pro を使用する必要があります。アプリストアよりダウンロードしてください。" "Element Pro が必要です" @@ -27,11 +27,11 @@ "サーバーのアドレスは何ですか?" "サーバーを選択" "アカウントを作成" - "このアカウントは無効化されています。" + "アカウントは削除されました。" "ユーザー名またはパスワードが違います" "無効なユーザーIDです。正しい形式は \"@ユーザー:ホームサーバー\" です。" "このサーバーはリフレッシュトークンを使用します。パスワードを使用したログインとは併用できません。" - "指定したホームサーバはパスワードまたはOIDCによるログインに対応していません。管理者に問い合わせるか、異なるホームサーバーを使用してください。" + "指定したホームサーバはパスワードまたはOAuthによるログインに対応していません。管理者に問い合わせるか、異なるホームサーバーを使用してください。" "詳細を入力" "Matrix は安全で分散型のオープンなネットワークです。" "お待ちしておりました。" @@ -80,8 +80,8 @@ "%1$s に非対応" "読み取る" "コンピュータで %1$s を開く" - "アバターをタップしてください" - "%1$s を選択してください" + "アバターをタップ" + "%1$s を選択" "\"新しい端末を追加\"" "この端末でQRコードを読み取る" "アカウント提供元が対応する場合にのみ使用できます。" @@ -98,11 +98,11 @@ "一方の端末を待機しています" "アカウント提供元が、サインインを検証するために以下の文字列を要求することがあります。" "検証コード" - "アカウントの提供元を変更" + "アカウント提供元を変更" "Element 開発者用の非公開のサーバーです。" "Matrix は安全で分散型のオープンなネットワークです。" - "メールアプリのように、あなたの会話はここに保管されています。" + "メールアプリのように、あなたの会話はこのサーバー上に保管されます。" "%1$s にサインインを試みています" "アカウント提供元を選択" - "%1$s 上にアカウントの作成を試みています" + "%1$s 上にアカウントを作成しようとしています" diff --git a/features/login/impl/src/main/res/values-ka/translations.xml b/features/login/impl/src/main/res/values-ka/translations.xml index 54e277ec12..04db3fda13 100644 --- a/features/login/impl/src/main/res/values-ka/translations.xml +++ b/features/login/impl/src/main/res/values-ka/translations.xml @@ -23,7 +23,7 @@ "არასწორი მომხმარებლის სახელი და/ან პაროლი" "მოცემული მომხმარებლის იდენტიფიკატორი არასწორია. დასაშვები ფორმატი: ‘@user:homeserver.org’" "ეს სერვერი კონფიგურირებულია განახლების გასაღებების გამოსაყენებლად. პაროლზე დაფუძნებული შეცვლისას ისინი მხარდაჭერილი არაა." - "მოცემული სახლის სერვერი მხარს არ უჭერს პაროლით ან OIDC-ით შესვლას. გთხოვთ, დაუკავშირდეთ თქვენს ადმინისტრატორს ან აარჩიეთ სხვა სახლის სერვერი." + "მოცემული სახლის სერვერი მხარს არ უჭერს პაროლით ან OAuth-ით შესვლას. გთხოვთ, დაუკავშირდეთ თქვენს ადმინისტრატორს ან აარჩიეთ სხვა სახლის სერვერი." "შეიყვანეთ თქვენი დეტალები" "Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის." "კეთილი იყოს თქვენი მობრძანება!" diff --git a/features/login/impl/src/main/res/values-ko/translations.xml b/features/login/impl/src/main/res/values-ko/translations.xml index c870377fc4..338ba628c9 100644 --- a/features/login/impl/src/main/res/values-ko/translations.xml +++ b/features/login/impl/src/main/res/values-ko/translations.xml @@ -32,7 +32,7 @@ "잘못된 아이디/비밀번호" "이 사용자 ID는 유효하지 않습니다. 예상 형식: ‘@user:homeserver.org’" "이 서버는 새로 고침 토큰을 사용하도록 구성되어 있습니다. 비밀번호 기반 로그인을 사용하는 경우 이 기능은 지원되지 않습니다." - "선택한 홈 서버는 password 또는 OIDC 로그인을 지원하지 않습니다. 관리자에게 문의하거나 다른 홈 서버를 선택하세요." + "선택한 홈 서버는 password 또는 OAuth 로그인을 지원하지 않습니다. 관리자에게 문의하거나 다른 홈 서버를 선택하세요." "귀하의 세부 정보를 입력하십시오" "Matrix 는 안전하고 분산된 커뮤니케이션을 위한 개방형 네트워크입니다." "다시 돌아온 걸 환영합니다!" diff --git a/features/login/impl/src/main/res/values-lt/translations.xml b/features/login/impl/src/main/res/values-lt/translations.xml index 870e2c4d31..dc1a2b7ba6 100644 --- a/features/login/impl/src/main/res/values-lt/translations.xml +++ b/features/login/impl/src/main/res/values-lt/translations.xml @@ -31,7 +31,7 @@ "Ši paskyra buvo išjungta." "Neteisingas vartotojo vardas ir (arba) slaptažodis" "Tai nėra tinkamas vartotojo vardas. Reikalingas formatas: \'@vartotojas:serveris.org\'" - "Pasirinktas serveris nepalaiko slaptažodžio ar OIDC prisijungimo. Susisiekite su serverio administracija arba pasirinkite kitą serverį." + "Pasirinktas serveris nepalaiko slaptažodžio ar OAuth prisijungimo. Susisiekite su serverio administracija arba pasirinkite kitą serverį." "Įveskite savo duomenis" "Matrix yra atviras tinklas, skirtas saugiam, decentralizuotam bendravimui." "Sveiki sugrįžę!" diff --git a/features/login/impl/src/main/res/values-nb/translations.xml b/features/login/impl/src/main/res/values-nb/translations.xml index 5957b902ce..6645691b84 100644 --- a/features/login/impl/src/main/res/values-nb/translations.xml +++ b/features/login/impl/src/main/res/values-nb/translations.xml @@ -32,7 +32,7 @@ "Feil brukernavn og/eller passord" "Dette er ikke en gyldig brukeridentifikator. Forventet format: \'@bruker:homeserver.org\'" "Denne serveren er konfigurert til å bruke oppdateringstokener. Disse støttes ikke når du bruker passordbasert pålogging." - "Den valgte hjemmeserveren støtter ikke passord eller OIDC-pålogging. Ta kontakt med administratoren din eller velg en annen hjemmeserver." + "Den valgte hjemmeserveren støtter ikke passord eller OAuth-pålogging. Ta kontakt med administratoren din eller velg en annen hjemmeserver." "Skriv inn opplysningene dine" "Matrix er et åpent nettverk for sikker, desentralisert kommunikasjon." "Velkommen tilbake!" 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 8a7f695156..e5d90e2234 100644 --- a/features/login/impl/src/main/res/values-nl/translations.xml +++ b/features/login/impl/src/main/res/values-nl/translations.xml @@ -24,7 +24,7 @@ "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." - "De geselecteerde homeserver ondersteunt geen wachtwoord of OIDC aanmelding. Neem contact op met je beheerder of kies een andere homeserver." + "De geselecteerde homeserver ondersteunt geen wachtwoord of OAuth aanmelding. Neem contact op met je beheerder of kies een andere homeserver." "Vul je gegevens in" "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie." "Welkom terug!" 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 0c7810abe7..b134395adf 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -32,7 +32,7 @@ "Nieprawidłowa nazwa użytkownika i/lub hasło" "To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'" "Ten serwer został skonfigurowany do korzystania z tokenów odświeżania. Nie są one obsługiwane, gdy korzystasz z hasła." - "Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OIDC. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy." + "Wybrany serwer domowy nie obsługuje uwierzytelniania hasłem, ani OAuth. Skontaktuj się z jego administratorem lub wybierz inny serwer domowy." "Wprowadź swoje dane" "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji." "Witaj ponownie!" diff --git a/features/login/impl/src/main/res/values-pt-rBR/translations.xml b/features/login/impl/src/main/res/values-pt-rBR/translations.xml index afca14d201..ad2ad2b5fb 100644 --- a/features/login/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/login/impl/src/main/res/values-pt-rBR/translations.xml @@ -32,7 +32,7 @@ "Nome de usuário e/ou senha incorretos" "Esse não é um identificador de usuário válido. Formato esperado: \'@usuário:servidor.org\'" "Este servidor está configurado para usar tokens recarregados. Não há suporte a eles ao entrar por uma senha." - "O servidor selecionado não suporta a entrada por senha ou OIDC. Entre em contato com o administrador ou escolha outro servidor." + "O servidor selecionado não suporta a entrada por senha ou OAuth. Entre em contato com o administrador ou escolha outro servidor." "Digite seus dados" "A Matrix é uma rede aberta para comunicação segura e descentralizada." "Boas-vindas novamente!" diff --git a/features/login/impl/src/main/res/values-pt/translations.xml b/features/login/impl/src/main/res/values-pt/translations.xml index c2aa0d5ed6..ff3cae843d 100644 --- a/features/login/impl/src/main/res/values-pt/translations.xml +++ b/features/login/impl/src/main/res/values-pt/translations.xml @@ -28,11 +28,11 @@ "Qual é o endereço do teu servidor?" "Seleciona o teu servidor" "Criar conta" - "Esta conta foi desativada." + "Esta conta foi eliminada." "Nome de utilizador ou senha incorretos" "Identificador de utilizador inválido. Formato esperado: ‘@utilizador:servidor.org’" "Este servidor está configurado para utilizar \"tokens\" de atualização. Estes não são suportados quando utilizas o início de sessão por senha." - "O servidor selecionado não suporta início de sessão por senha nem por OIDC. Por favor, contacta o teu administrador ou escolhe outro servidor." + "O servidor selecionado não suporta início de sessão por senha nem por OAuth. Por favor, contacta o teu administrador ou escolhe outro servidor." "Insere o teus detalhes" "A Matrix é uma rede aberta de comunicação descentralizada e segura." "Bem-vindo(a) de volta!" diff --git a/features/login/impl/src/main/res/values-ro/translations.xml b/features/login/impl/src/main/res/values-ro/translations.xml index 6dddc4d0bf..ec9b260208 100644 --- a/features/login/impl/src/main/res/values-ro/translations.xml +++ b/features/login/impl/src/main/res/values-ro/translations.xml @@ -32,7 +32,7 @@ "Utilizator și/sau parolă incorecte" "Acesta nu este un identificator de utilizator valid. Format așteptat: „@user:homeserver.org”" "Acest server este configurat pentru a utiliza token-uri de reîmprospătare. Acestea nu sunt acceptate atunci când utilizați autentificare bazată pe parolă." - "Homeserver-ul selectat nu acceptă autentificarea prin parola sau OIDC. Te rugăm să contactezi administratorul sau să alegi un alt homeserver." + "Homeserver-ul selectat nu acceptă autentificarea prin parola sau OAuth. Te rugăm să contactezi administratorul sau să alegi un alt homeserver." "Introduceți detaliile" "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată." "Bine ați revenit!" diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index 4437c19056..329bad30af 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -32,7 +32,7 @@ "Неверное имя пользователя и/или пароль" "Это некорректный идентификатор пользователя. Правильный формат: @user:homeserver.org" "Этот сервер настроен на использование токенов обновления. Они не поддерживаются при использовании входа на основе пароля." - "Выбранный сервер не поддерживает вход по паролю и OIDC. Пожалуйста, свяжитесь с администратором или выберите другой сервер." + "Выбранный сервер не поддерживает вход по паролю и OAuth. Пожалуйста, свяжитесь с администратором или выберите другой сервер." "Введите свои данные" "Matrix — это открытая сеть для безопасной децентрализованной связи." "Рады видеть вас снова!" diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml index 9bd494dd56..b2c3077de2 100644 --- a/features/login/impl/src/main/res/values-sk/translations.xml +++ b/features/login/impl/src/main/res/values-sk/translations.xml @@ -32,7 +32,7 @@ "Nesprávne používateľské meno a/alebo heslo" "Toto nie je platný identifikátor používateľa. Očakávaný formát: \'@pouzivatel:homeserver.sk\'" "Tento server je nakonfigurovaný tak, aby používal obnovovacie tokeny. Pri prihlasovaní na základe hesla nie sú podporované." - "Vybraný domovský server nepodporuje prihlásenie pomocou hesla alebo OIDC. Obráťte sa na správcu alebo vyberte iný domovský server." + "Vybraný domovský server nepodporuje prihlásenie pomocou hesla alebo OAuth. Obráťte sa na správcu alebo vyberte iný domovský server." "Zadajte svoje údaje" "Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu." "Vitajte späť!" diff --git a/features/login/impl/src/main/res/values-sv/translations.xml b/features/login/impl/src/main/res/values-sv/translations.xml index 33fb76b5bd..396f2025b7 100644 --- a/features/login/impl/src/main/res/values-sv/translations.xml +++ b/features/login/impl/src/main/res/values-sv/translations.xml @@ -32,7 +32,7 @@ "Felaktigt användarnamn och/eller lösenord" "Detta är inte en giltig användaridentifierare. Förväntat format: \'@användare:hemserver.org\'" "Den här servern är konfigurerad för att använda uppdateringstokens. Dessa stöds inte när du använder lösenordsbaserad inloggning." - "Den valda hemservern stöder inte lösenord eller OIDC-inloggning. Kontakta administratören eller välj en annan hemserver." + "Den valda hemservern stöder inte lösenord eller OAuth-inloggning. Kontakta administratören eller välj en annan hemserver." "Ange dina uppgifter" "Matrix är ett öppet nätverk för säker, decentraliserad kommunikation." "Välkommen tillbaka!" diff --git a/features/login/impl/src/main/res/values-tr/translations.xml b/features/login/impl/src/main/res/values-tr/translations.xml index 1574fca3a8..05bb9bab15 100644 --- a/features/login/impl/src/main/res/values-tr/translations.xml +++ b/features/login/impl/src/main/res/values-tr/translations.xml @@ -28,7 +28,7 @@ "Yanlış kullanıcı adı ve/veya şifre" "Bu geçerli bir kullanıcı tanımlayıcısı değil. Kullanılması gereken biçim: \'@user:homeserver.org\'" "Bu sunucu, yenileme belirteçlerini kullanacak şekilde yapılandırılmıştır. Parola tabanlı oturum açma kullanılırken bunlar desteklenmez." - "Seçilen ana sunucu parola veya OIDC oturum açmayı desteklemiyor. Lütfen yöneticinizle iletişime geçin veya başka bir ana sunucu seçin." + "Seçilen ana sunucu parola veya OAuth oturum açmayı desteklemiyor. Lütfen yöneticinizle iletişime geçin veya başka bir ana sunucu seçin." "Bilgilerinizi girin" "Matrix, güvenli, merkezi olmayan iletişim için açık bir ağdır." "Tekrar hoş geldiniz!" diff --git a/features/login/impl/src/main/res/values-uk/translations.xml b/features/login/impl/src/main/res/values-uk/translations.xml index b93a66fed2..0d8eca42cf 100644 --- a/features/login/impl/src/main/res/values-uk/translations.xml +++ b/features/login/impl/src/main/res/values-uk/translations.xml @@ -32,7 +32,7 @@ "Неправильне ім\'я користувача та/або пароль" "Це недійсний ідентифікатор користувача. Очікуваний формат: \'@user:homeserver.org\'" "Цей сервер налаштований на використання оновлюваних токенів. Вони не підтримуються, якщо використовується вхід за допомогою основі пароля." - "Обраний домашній сервер не підтримує вхід за допомогою пароля або OIDC. Зверніться до адміністратора або виберіть інший домашній сервер." + "Обраний домашній сервер не підтримує вхід за допомогою пароля або OAuth. Зверніться до адміністратора або виберіть інший домашній сервер." "Введіть свої дані" "Matrix — це відкрита мережа для безпечної, децентралізованої комунікації." "З поверненням!" diff --git a/features/login/impl/src/main/res/values-ur/translations.xml b/features/login/impl/src/main/res/values-ur/translations.xml index 334d5b6ea6..6762ab16af 100644 --- a/features/login/impl/src/main/res/values-ur/translations.xml +++ b/features/login/impl/src/main/res/values-ur/translations.xml @@ -24,7 +24,7 @@ "غلط صارف نام اور/یا لفظ عبور" "یہ صالح صارف شناسه نہیں ہے۔ متوقع شکل: @صارف:منزلی خادم" "یہ خادم تازگی کی رموزِ ممیز استعمال کرنے کے لئے تشکیل دیا گیا ہے۔ لفظ عبور پر مبنی دخول استعمال کرتے ہوئے ان کی حمایت نہیں کی جاتی۔" - "منتخب منزلی خادم کلمۂ عبوری یا OIDC دخول کا تعاون نہیں کرتا۔ برائے مہربانی اپنے منتظم سے رابطہ کریں یا کوئی اور منزلی خادم چنیں۔" + "منتخب منزلی خادم کلمۂ عبوری یا OAuth دخول کا تعاون نہیں کرتا۔ برائے مہربانی اپنے منتظم سے رابطہ کریں یا کوئی اور منزلی خادم چنیں۔" "اپنی تفصیلات درج کریں" "میٹرکس محفوظ، غیر مرکزی مواصلت کے لئے ایک کھلا شبکہ ہے۔" "واپس خوش آمدید!" diff --git a/features/login/impl/src/main/res/values-uz/translations.xml b/features/login/impl/src/main/res/values-uz/translations.xml index 07b3d8835b..546397935b 100644 --- a/features/login/impl/src/main/res/values-uz/translations.xml +++ b/features/login/impl/src/main/res/values-uz/translations.xml @@ -31,7 +31,7 @@ "Notog\'ri foydalanuvchi nomi va/yoki parol" "Bu haqiqiy foydalanuvchi identifikatori emas. Kutilayotgan format: \'@user:homeserver.org\'" "Ushbu server yangilash tokenlaridan foydalanishga moslashtirilgan. Parolga asoslangan tizimga kirishda bunday tokenlar qoʻllab-quvvatlanmaydi." - "Tanlangan uy serveri parol yoki OIDC loginni qo\'lab-quvvatlamaydi. Iltimos, administratoringizga murojaat qiling yoki boshqa uy serverini tanlang." + "Tanlangan uy serveri parol yoki OAuth loginni qo\'lab-quvvatlamaydi. Iltimos, administratoringizga murojaat qiling yoki boshqa uy serverini tanlang." "Tafsilotlaringizni kiriting" "Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir." "Qaytib kelganingizdan xursandmiz!" diff --git a/features/login/impl/src/main/res/values-vi/translations.xml b/features/login/impl/src/main/res/values-vi/translations.xml index 4d9b921ea5..66089a4708 100644 --- a/features/login/impl/src/main/res/values-vi/translations.xml +++ b/features/login/impl/src/main/res/values-vi/translations.xml @@ -31,7 +31,7 @@ "Tên người dùng và/hoặc mật khẩu không chính xác" "Đây không phải là mã nhận dạng người dùng hợp lệ. Định dạng mong đợi: ‘@user:homeserver.org’" "Máy chủ này được cấu hình sử dụng refresh token. Điều này không được hỗ trợ khi đăng nhập bằng mật khẩu." - "Homeserver đã chọn không hỗ trợ đăng nhập bằng mật khẩu hoặc OIDC. Vui lòng liên hệ với quản trị viên của cậu hoặc chọn một homeserver khác." + "Homeserver đã chọn không hỗ trợ đăng nhập bằng mật khẩu hoặc OAuth. Vui lòng liên hệ với quản trị viên của cậu hoặc chọn một homeserver khác." "Nhập thông tin chi tiết của bạn." "Matrix là một mạng mở cho việc liên lạc an toàn và phi tập trung." "Chào mừng bạn quay trở lại!" diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml index 7170288c5d..c4d16f5928 100644 --- a/features/login/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml @@ -32,7 +32,7 @@ "不正確的使用者名稱或密碼" "此非有效的使用者識別字串。預期的格式:‘@user:homeserver.org’" "此伺服器已設定為使用重新整理權杖。使用以密碼為基礎的登入方式時,不支援這些功能。" - "選定的家伺服器不支援密碼或 OIDC 登入。請聯絡您的管理員或選擇其他家伺服器。" + "選定的家伺服器不支援密碼或 OAuth 登入。請聯絡您的管理員或選擇其他家伺服器。" "輸入您的詳細資料" "Matrix 是一個開放網路,為了安全且去中心化的通訊而生。" "歡迎回來!" diff --git a/features/login/impl/src/main/res/values-zh/translations.xml b/features/login/impl/src/main/res/values-zh/translations.xml index ac125ba945..8e86b51d7b 100644 --- a/features/login/impl/src/main/res/values-zh/translations.xml +++ b/features/login/impl/src/main/res/values-zh/translations.xml @@ -21,18 +21,18 @@ %1$s" "所选账户提供者不支持滑动同步。需要升级服务器才能使用 %1$s。" "%1$s 不允许连接到 %2$s。" - "本应用已配置为允许访问:%1$s 。" + "此 app 已配置为允许访问:%1$s。" "账户提供者 %1$s 不被允许。" "主服务器 URL" "输入域名地址。" "你的服务器地址是什么?" "选择服务器" "创建账户" - "该账户已被停用。" + "此账户已被删除。" "用户名与(或)密码不正确" "这不是合法的用户 ID。预期格式:“@user:homeserver.org”。" "此服务器使用刷新令牌。使用密码登录时不支持这些功能。" - "该服务器不支持密码登录与 OIDC 登录。请联系服务器管理员或选择另一服务器。" + "该服务器不支持密码登录与 OAuth 登录。请联系服务器管理员或选择另一服务器。" "输入详细信息" "Matrix 是一个用于安全、去中心化通信的开放网络。" "欢迎回来!" @@ -51,7 +51,7 @@ "使用二维码登录" "创建账户" "欢迎回来" - "欢迎使用 %1$s,快而简约的消息应用。" + "欢迎使用迄今最快的 %1$s,速度与简洁的极致。" "欢迎使用 %1$s,速度与简洁的极致。" "融入 Element" "建立安全连接" diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml index f8d5ffa651..10fb6ef04a 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -32,7 +32,7 @@ "Incorrect username and/or password" "This is not a valid user identifier. Expected format: ‘@user:homeserver.org’" "This server is configured to use refresh tokens. These aren\'t supported when using password based login." - "The selected homeserver doesn\'t support password or OIDC login. Please contact your admin or choose another homeserver." + "The selected homeserver doesn\'t support password or OAuth login. Please contact your admin or choose another homeserver." "Enter your details" "Matrix is an open network for secure, decentralised communication." "Welcome back!" diff --git a/features/logout/impl/src/main/res/values-de/translations.xml b/features/logout/impl/src/main/res/values-de/translations.xml index 9021aee4da..0218642abc 100644 --- a/features/logout/impl/src/main/res/values-de/translations.xml +++ b/features/logout/impl/src/main/res/values-de/translations.xml @@ -4,15 +4,15 @@ "Dieses Gerät entfernen" "Dieses Gerät entfernen" "Gerät wird entfernt…" - "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten." - "Du hast das Backup deaktiviert" - "Das Backup deiner Schlüssel lief noch, als du offline gegangen bist. Verbinde dich erneut, damit deine Schlüssel vor dem Abmelden gesichert werden können." + "Dies ist dein einziges Gerät. Wenn du es entfernst, benötigst du einen Wiederherstellungsschlüssel, um deine digitale Identität zu bestätigen und deine verschlüsselten Chats bei deiner nächsten Anmeldung wiederherzustellen." + "Du bist dabei, den Zugriff auf deine verschlüsselten Chats zu verlieren" + "Deine Schlüssel wurden noch gesichert, während du offline gegangen bist. Stelle die Verbindung wieder her, damit deine Schlüssel gesichert werden können, bevor du dieses Gerät entfernst." "Deine Schlüssel werden noch gesichert" "Bitte warte, bis der Vorgang abgeschlossen ist, bevor du dieses Gerät entfernst." "Deine Schlüssel werden noch gesichert" "Dieses Gerät entfernen" - "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten." + "Dies ist dein einziges Gerät. Wenn du es entfernst, benötigst du einen Wiederherstellungsschlüssel, um deine digitale Identität zu bestätigen und deine verschlüsselten Chats bei deiner nächsten Anmeldung wiederherzustellen." "Du bist dabei, den Zugriff auf deine verschlüsselten Chats zu verlieren" - "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du möglicherweise den Zugriff auf deine verschlüsselten Nachrichten." + "Dies ist dein einziges Gerät. Wenn du es entfernst, benötigst du einen Wiederherstellungsschlüssel, um deine digitale Identität zu bestätigen und deine verschlüsselten Chats bei deiner nächsten Anmeldung wiederherzustellen." "Stelle sicher, dass du Zugriff auf deinen Wiederherstellungsschlüssel hast, bevor du dieses Gerät entfernst" diff --git a/features/logout/impl/src/main/res/values-fa/translations.xml b/features/logout/impl/src/main/res/values-fa/translations.xml index 8dfaad8580..d286ded539 100644 --- a/features/logout/impl/src/main/res/values-fa/translations.xml +++ b/features/logout/impl/src/main/res/values-fa/translations.xml @@ -1,16 +1,16 @@ - "مطمئنید که می‌خواهید از حسابتان خارج شوید؟" - "خروج" - "خروج" - "خارج شدن…" + "مطمئنید که می‌خواهید این افزاره را بردارید؟" + "برداشتن این افزاره" + "برداشتن این افزاره" + "برداشتن افزاره…" "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید پیام‌های رمزنگاشته‌تان را از دست خواهید داد." "پشتیبان را خاموش کرده‌اید" "در هنگامی که آفلاین شدید، کلیدهای شما هنوز در حال پشتیبان‌گیری بودند. دوباره متصل شوید ، تا قبل از خروج از کلیدهایتان نسخه پشتیبان‌ گرفته شود." "کلیدهایتان هنوز در حال پشتیبان گیریند" "لطفاً پیش از خروج منتظر پایانش شوید." "کلیدهایتان هنوز در حال پشتیبان گیریند" - "خروج" + "برداشتن این افزاره" "شما در آستانه خروج از آخرین جلسه خود هستید. اگر اکنون از سیستم خارج شوید، دسترسی به پیام های رمزگذاری شده تان را از دست خواهید داد." "بازگردانی برپا نشده" "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید ممکن است پیام‌های رمزنگاشته‌تان را از دست بدهید." diff --git a/features/logout/impl/src/main/res/values-in/translations.xml b/features/logout/impl/src/main/res/values-in/translations.xml index dabf83545e..9d63573973 100644 --- a/features/logout/impl/src/main/res/values-in/translations.xml +++ b/features/logout/impl/src/main/res/values-in/translations.xml @@ -1,16 +1,16 @@ - "Apakah Anda yakin ingin keluar dari akun?" - "Keluar dari akun" - "Keluar dari akun" - "Mengeluarkan dari akun…" + "Apakah Anda yakin ingin non aktifkan device dari akun?" + "Hapus device dari akun" + "Hapus device dari akun" + "Mengeluarkan device dari akun…" "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda." "Anda telah menonaktifkan pencadangan" "Kunci Anda masih dicadangkan saat Anda luring. Sambungkan kembali sehingga kunci Anda dapat dicadangkan sebelum keluar." "Kunci Anda masih dicadangkan" "Mohon tunggu hingga proses ini selesai sebelum keluar." "Kunci Anda masih dicadangkan" - "Keluar dari akun" + "Hapus device dari akun" "Anda akan keluar dari sesi Anda yang terakhir. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda." "Pemulihan belum disiapkan" "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda mungkin kehilangan akses ke pesan terenkripsi Anda." diff --git a/features/logout/impl/src/main/res/values-pt/translations.xml b/features/logout/impl/src/main/res/values-pt/translations.xml index b8a7161c21..76e4f09d42 100644 --- a/features/logout/impl/src/main/res/values-pt/translations.xml +++ b/features/logout/impl/src/main/res/values-pt/translations.xml @@ -1,18 +1,18 @@ - "Tens a certeza que queres terminar a sessão?" - "Terminar sessão" - "Terminar sessão" - "A terminar sessão…" - "Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas." - "Desativaste a cópia de segurança" - "As tuas chaves ainda estavam a ser guardadas quando ficaste desligado. Volta a ligar-te para que as tuas chaves possam ser guardadas antes de encerrares a sessão." + "Tens a certeza que queres remover este dispositivo?" + "Remover este dispositivo" + "Remover este dispositivo" + "A remover dispositivo…" + "Este é o teu único dispositivo. Se o removeres, da próxima vez que iniciares sessão, precisarás da chave de recuperação para confirmares a tua identidade digital e recuperares as tuas conversas cifradas." + "Estás prestes a perder o acesso às tuas conversas privadas" + "As tuas chaves ainda estavam a ser guardadas quando ficaste desligado. Volta a ligar-te para que as tuas chaves possam ser guardadas antes de removeres o dispositivo." "As tuas chaves ainda estão a ser guardadas" - "Por favor, aguarda a conclusão desta operação antes de terminares a sessão." + "Por favor, aguarda a conclusão desta operação antes de removeres o dispositivo." "As tuas chaves ainda estão a ser guardadas" - "Terminar sessão" - "Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas." - "Recuperação não configurada" - "Estás prestes a terminar a tua última sessão. Se continuares, poderás perder o acesso às tuas mensagens cifradas." - "Guardaste a tua chave de recuperação?" + "Remover este dispositivo" + "Este é o teu único dispositivo. Se o removeres, da próxima vez que iniciares sessão, precisarás da chave de recuperação para confirmares a tua identidade digital e recuperares as tuas conversas cifradas." + "Estás prestes a perder o acesso às tuas conversas cifradas" + "Este é o teu único dispositivo. Se o removeres, da próxima vez que iniciares sessão, precisarás da chave de recuperação para confirmares a tua identidade digital e recuperares as tuas conversas cifradas." + "Certifica-te de que tens acesso à tua chave de recuperação antes de removeres este dispositivo" diff --git a/features/logout/impl/src/main/res/values-zh/translations.xml b/features/logout/impl/src/main/res/values-zh/translations.xml index 3370b40f3c..f18904d03b 100644 --- a/features/logout/impl/src/main/res/values-zh/translations.xml +++ b/features/logout/impl/src/main/res/values-zh/translations.xml @@ -1,16 +1,16 @@ "你确定要移除此设备?" - "删除此设备" - "删除此设备" - "正在删除设备……" + "移除此设备" + "移除此设备" + "正在移除设备…" "这是你的唯一设备。一旦移除,下次登录时你需要使用恢复密钥验证数字身份并恢复加密聊天。" "你即将失去加密聊天的访问权" "当你离线时,密钥仍在备份。重新连接以便在移除设备之前备份密钥。" "你的密钥仍在备份中" "请等待此操作完成再移除此设备。" "你的密钥仍在备份中" - "删除此设备" + "移除此设备" "这是你的唯一设备。一旦移除,下次登录时你需要使用恢复密钥验证数字身份并恢复加密聊天。" "你即将失去加密聊天的访问权" "这是你的唯一设备。一旦移除,下次登录时你需要使用恢复密钥验证数字身份并恢复加密聊天。" diff --git a/features/messages/impl/src/main/res/values-da/translations.xml b/features/messages/impl/src/main/res/values-da/translations.xml index 390d03f451..e068df293a 100644 --- a/features/messages/impl/src/main/res/values-da/translations.xml +++ b/features/messages/impl/src/main/res/values-da/translations.xml @@ -35,7 +35,7 @@ "Optag video" "Vedhæftning" "Foto- og videobibliotek" - "Del lokation" + "Del placering" "Afstemning" "Tekstformatering" "Beskedhistorikken er i øjeblikket ikke tilgængelig." diff --git a/features/messages/impl/src/main/res/values-fa/translations.xml b/features/messages/impl/src/main/res/values-fa/translations.xml index 446239c095..dcac058d2f 100644 --- a/features/messages/impl/src/main/res/values-fa/translations.xml +++ b/features/messages/impl/src/main/res/values-fa/translations.xml @@ -27,7 +27,7 @@ "ضبط ویدیو" "پیوست" "کتابخانهٔ عکس و ویدیو" - "مکان" + "هم‌رسانی مکان" "نظرسنجی" "قالب‌بندی متن" "تاریخچه پیام درحال حاضر دردسترس نیست." diff --git a/features/messages/impl/src/main/res/values-in/translations.xml b/features/messages/impl/src/main/res/values-in/translations.xml index 508f4d5476..7a0994f65e 100644 --- a/features/messages/impl/src/main/res/values-in/translations.xml +++ b/features/messages/impl/src/main/res/values-in/translations.xml @@ -34,7 +34,7 @@ "Rekam video" "Lampiran" "Pustaka Foto & Video" - "Lokasi" + "Membagi Lokasi" "Jajak pendapat" "Pemformatan Teks" "Riwayat pesan saat ini tidak tersedia di ruangan ini" diff --git a/features/messages/impl/src/main/res/values-ja/translations.xml b/features/messages/impl/src/main/res/values-ja/translations.xml index 4fd18e8db5..037c0f7d67 100644 --- a/features/messages/impl/src/main/res/values-ja/translations.xml +++ b/features/messages/impl/src/main/res/values-ja/translations.xml @@ -34,7 +34,7 @@ "写真を撮影" "動画を撮影" "添付ファイル" - "アルバムの写真・動画" + "アルバムの写真と動画" "場所を共有" "投票" "書式設定" diff --git a/features/messages/impl/src/main/res/values-pt/translations.xml b/features/messages/impl/src/main/res/values-pt/translations.xml index 054d2a1759..2fb5173169 100644 --- a/features/messages/impl/src/main/res/values-pt/translations.xml +++ b/features/messages/impl/src/main/res/values-pt/translations.xml @@ -35,7 +35,7 @@ "Gravar vídeo" "Anexo" "Biblioteca de fotos e vídeos" - "Localização" + "Partilhar localização" "Sondagem" "Formatação de texto" "De momento, o histórico de mensagens está indisponível." diff --git a/features/messages/impl/src/main/res/values-zh/translations.xml b/features/messages/impl/src/main/res/values-zh/translations.xml index 26518d656c..fc576aaf11 100644 --- a/features/messages/impl/src/main/res/values-zh/translations.xml +++ b/features/messages/impl/src/main/res/values-zh/translations.xml @@ -21,13 +21,13 @@ "无法上传该文件。" "处理要上传的媒体失败,请重试。" "上传媒体失败,请重试。" - "允许的最大文件大小为%1$s 。" + "允许的最大文件大小为 %1$s。" "文件太大,无法上传" "第 %1$d 个项目,共 %2$d 个" "优化图像质量" "处理中…" "屏蔽用户" - "请确认是否要隐藏该用户当前和未来的所有信息" + "请确认是否要隐藏该用户当前和未来的所有消息" "此消息将举报给服务器管理员。他们无法读取任何加密消息。" "举报此内容的理由" "相机" diff --git a/features/preferences/impl/src/main/res/values-da/translations.xml b/features/preferences/impl/src/main/res/values-da/translations.xml index f258fb0952..2f828a4063 100644 --- a/features/preferences/impl/src/main/res/values-da/translations.xml +++ b/features/preferences/impl/src/main/res/values-da/translations.xml @@ -11,6 +11,14 @@ "Skjul avatarer i anmodninger om invitation til rum" "Skjul forhåndsvisning af medier i tidslinjen" "Laboratorier" + "Den afstand, du skal tilbagelægge for at udløse en opdatering." + "Sørg for, at \"nøjagtig placering\" er aktiveret for denne app. For at ændre på tilladelsen skal du gå til %1$s." + "App-indstillinger" + "Live placeringsopdateringer" + + "Hver %1$d meter" + "Hver %1$d meter" + "Upload fotos og videoer hurtigere, og reducér dataforbrug" "Optimér mediekvaliteten" "Moderation og sikkerhed" diff --git a/features/preferences/impl/src/main/res/values-hr/translations.xml b/features/preferences/impl/src/main/res/values-hr/translations.xml index 250af33d49..a40a6feccd 100644 --- a/features/preferences/impl/src/main/res/values-hr/translations.xml +++ b/features/preferences/impl/src/main/res/values-hr/translations.xml @@ -11,6 +11,15 @@ "Sakrij avatare u zahtjevima za poziv u sobu" "Sakrij preglede medija na vremenskoj traci" "Laboratoriji" + "Udaljenost koju morate prijeći da biste pokrenuli ažuriranje." + "Provjerite je li \"Precizna lokacija\" omogućena za ovu aplikaciju. Za promjenu dopuštenja idite na %1$s ." + "Postavke aplikacije" + "Ažuriranja lokacije uživo" + + "Svaki %1$d metar" + "Svaki %1$d metar" + "Svaki %1$d metara" + "Brže prenesite fotografije i videozapise te smanjite potrošnju podataka" "Optimiziraj kvalitetu medija" "Moderiranje i sigurnost" 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 c588e60eaf..d75166df5e 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -11,6 +11,14 @@ "Profilképek elrejtése a szobameghívókban" "Médiaelőnézetek elrejtése az idővonalon" "Kísérletek" + "A megtett távolság miután a helyadat frissül." + "Győződjön meg arról, hogy a „Pontos helymeghatározás” engedélyezve van ehhez az alkalmazáshoz. Az engedély módosításához menjen ide:%1$s ." + "Alkalmazásbeállítások" + "Élő helymeghatározás" + + "Minden %1$d méter" + "Minden %1$d méter" + "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" "Moderálás és biztonság" diff --git a/features/preferences/impl/src/main/res/values-ja/translations.xml b/features/preferences/impl/src/main/res/values-ja/translations.xml index 10cbca0ada..22334c4120 100644 --- a/features/preferences/impl/src/main/res/values-ja/translations.xml +++ b/features/preferences/impl/src/main/res/values-ja/translations.xml @@ -18,9 +18,9 @@ "%1$dm ごと" - "写真や動画を高速で送信してデータ使用量を減らす" + "写真や動画を高速で送信してデータ使用量を減らします。" "メディアの品質を最適化" - "制限と安全" + "セキュリティと制限" "自動的に画像を最適化してアップロード時間とファイルサイズを削減します。" "画像のアップロード画質を最適化" "%1$s 変更するにはタップしてください。" @@ -53,7 +53,7 @@ "プロフィールを更新中…" "スレッドへの返信を有効化" "変更を適用するためにアプリケーションは再起動します。" - "開発段階の最新機能を試します。未完成のため変更や不安定な挙動を生じる可能性があります。" + "開発段階の最新機能を試すことができます。未完成のため、変更や不安定な挙動が生じる可能性があります。" "探究したいですか?" "ラボ" "追加設定" @@ -68,7 +68,7 @@ "すべてのメッセージ" "メンションとキーワードのみ" "ダイレクトチャットで以下の通知を受け取る" - "グループチャットでは以下の通知を受け取る" + "グループチャットで以下の通知を受け取る" "この端末で通知を受け取る" "設定が修正されていません。再試行してください。" "グループチャット" @@ -77,7 +77,7 @@ "メンション" "すべて" "メンション" - "以下を通知する" + "以下を通知" @ルームで通知を受け取る "通知を受け取るには、%1$s を変更してください。" "システム設定" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index d57fb3b740..41eb147141 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -12,7 +12,7 @@ "在时间线上隐藏媒体预览" "实验室" "触发一次更新所需的移动距离。" - "确保已为 app 启用“精确位置”。如需更改该权限请转到 %1$s 。" + "确保已为 app 启用“精确位置”。如需更改该权限请转到 %1$s。" "App 设置" "实时位置更新" @@ -44,13 +44,13 @@ "解除屏蔽" "可以重新接收他们的消息。" "解除屏蔽用户" - "正在解除屏蔽……" + "正在解除屏蔽…" "显示名称" "你的显示名称" "遇到未知错误,无法更改信息。" "无法更新个人资料" "编辑个人资料" - "更新个人资料……" + "正在更新个人资料…" "启用消息列中的回复" "App 将重启以应用此更改。" "尝试我们最新的开发理念。这些功能尚未最终确定,可能不稳定,也可能会发生变化。" diff --git a/features/rageshake/api/src/main/res/values-zh/translations.xml b/features/rageshake/api/src/main/res/values-zh/translations.xml index ca81dd2d2c..8c78bbbc9c 100644 --- a/features/rageshake/api/src/main/res/values-zh/translations.xml +++ b/features/rageshake/api/src/main/res/values-zh/translations.xml @@ -1,6 +1,6 @@ - "%1$s 上次使用时崩溃了。想和我们分享崩溃报告吗?" + "%1$s 上次使用时曾崩溃过。是否与我们分享崩溃报告?" "你似乎愤怒地摇晃了手机。是否打开 Bug 报告页面?" "摇一摇" "检测阈值" diff --git a/features/rageshake/impl/src/main/res/values-zh/translations.xml b/features/rageshake/impl/src/main/res/values-zh/translations.xml index b34badea08..c42141d291 100644 --- a/features/rageshake/impl/src/main/res/values-zh/translations.xml +++ b/features/rageshake/impl/src/main/res/values-zh/translations.xml @@ -13,7 +13,7 @@ "日志文件过大,无法包含在本次报告中,请通过其它方式发送给我们。" "发送屏幕截图" "为确认一切正常运行,日志中将包含你的消息。如要发送不含消息的日志,请关闭此设置。" - "%1$s 上次使用时崩溃了。想和我们分享崩溃报告吗?" + "%1$s 上次使用时曾崩溃过。是否与我们分享崩溃报告?" "如果你遭遇通知相关问题,上传通知设置可以帮助我们调查根本原因。请注意:这些规则可能包含私人信息,例如你的显示名称或用于接收通知的关键词。" "发送通知设置" "查看日志" diff --git a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml index c526d059bc..2bd79bddd2 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml @@ -10,7 +10,7 @@ "مدیرن و ناظران" "برداشتن افراد و رد درخواست‌های پیوستن" "تغییر چهرک اتاق" - "ویرایش اتاق" + "ویرایش جزییات" "تغییر نام اتاق" "دگرگونی موضوع اتاق" "فرستادن پیام‌ها" diff --git a/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml index 9d792fcc74..b28cf8e524 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml @@ -1,7 +1,7 @@ "管理员" - "封禁成员" + "封禁人员" "更改设置" "移除消息" "成员" @@ -39,7 +39,7 @@ "保存更改?" "暂无被封禁的用户。" - "%1$d 被禁用" + "%1$d 人被封禁" "检查拼写或尝试新搜索" "未找到 “%1$s” 相关结果" diff --git a/features/roomdetails/impl/src/main/res/values-da/translations.xml b/features/roomdetails/impl/src/main/res/values-da/translations.xml index e381a30957..2217e0b9c6 100644 --- a/features/roomdetails/impl/src/main/res/values-da/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-da/translations.xml @@ -153,7 +153,7 @@ Vi anbefaler ikke at aktivere kryptering for rum, som alle kan finde og deltage "Adgang" "Alle i autoriserede klynger kan deltage." "Alle i %1$s kan deltage." - "Medlemmer af rummet" + "Medlemmer af klyngen" "Klynger understøttes ikke i øjeblikket" "Du skal bruge en adresse for at gøre det synligt i det offentlige register." "Adresse" diff --git a/features/roomdetails/impl/src/main/res/values-fa/translations.xml b/features/roomdetails/impl/src/main/res/values-fa/translations.xml index bfe743fd1b..364c94c6f2 100644 --- a/features/roomdetails/impl/src/main/res/values-fa/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fa/translations.xml @@ -14,7 +14,7 @@ "مدیرن و ناظران" "برداشتن افراد و رد درخواست‌های پیوستن" "تغییر چهرک اتاق" - "ویرایش اتاق" + "ویرایش جزییات" "تغییر نام اتاق" "دگرگونی موضوع اتاق" "فرستادن پیام‌ها" @@ -40,7 +40,7 @@ "رمز شده" "رمزنگاری نشده" "اتاق عمومی" - "ویرایش اتاق" + "ویرایش جزییات" "خطایی ناشناخته رخ داد و اطّلاعات قابل تغییر نبودند." "ناتوان در به‌روز رسانی اتاق" "پیام‌ها با قفل محافظت می‌شوند. فقط شما و گیرندگان، کلیدهای منحصر به فرد برای باز کردن قفل آنها را دارید." diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index d2cc6773fb..267cdf5c04 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -9,7 +9,7 @@ "主服务器不支持在加密房间中的此选项,因此在某些房间你可能无法收到通知。" "投票" "管理员" - "封禁成员" + "封禁人员" "移除消息" "成员" "邀请人员" @@ -55,7 +55,7 @@ "无法取消静音此房间,请重试。" "完成之前请勿关闭 app。" "正在准备邀请…" - "邀请朋友" + "邀请人员" "离开聊天" "离开房间" "媒体与文件" @@ -75,7 +75,7 @@ "正在更新房间…" "暂无被封禁的用户。" - "%1$d 被禁用" + "%1$d 人被封禁" "检查拼写或尝试新搜索" "未找到 “%1$s” 相关结果" @@ -145,7 +145,7 @@ "任何人" "选择哪些无需邀请即可加入此房间的空间成员。%1$s" "管理空间" - "仅限受邀者加入。" + "仅限受邀人员加入。" "仅限受邀者" "访问权限" "任何位于已授权空间的成员均可加入。" diff --git a/features/roomdetailsedit/impl/src/main/res/values-fa/translations.xml b/features/roomdetailsedit/impl/src/main/res/values-fa/translations.xml index bbbd63d171..a339ecd57f 100644 --- a/features/roomdetailsedit/impl/src/main/res/values-fa/translations.xml +++ b/features/roomdetailsedit/impl/src/main/res/values-fa/translations.xml @@ -1,6 +1,6 @@ - "ویرایش اتاق" + "ویرایش جزییات" "خطایی ناشناخته رخ داد و اطّلاعات قابل تغییر نبودند." "ناتوان در به‌روز رسانی اتاق" "به‌روز کردن اتاق…" diff --git a/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml b/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml index 2c7e6584b8..bfd456f130 100644 --- a/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-zh/translations.xml @@ -13,7 +13,7 @@ "查看个人资料" "移除用户" "删除成员并禁止重新加入?" - "正在移除 %1$s……" + "正在移除 %1$s…" "解封用户" "解封" "如果他们受到邀请,则可以重新加入" 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 8363cb9754..d53086a6d1 100644 --- a/features/securebackup/impl/src/main/res/values-de/translations.xml +++ b/features/securebackup/impl/src/main/res/values-de/translations.xml @@ -2,9 +2,9 @@ "Backup deaktivieren" "Backup aktivieren" - "Speichere deine kryptographische Identität und die Nachrichtenschlüssel auf dem Server. Auf diese Weise kannst du deinen Nachrichtenverlauf auf neuen Geräten einsehen. %1$s." + "Dadurch kannst du deinen Chatverlauf auf allen neuen Geräten einsehen. Er ist außerdem für die Sicherung von Chats und deiner digitalen Identität erforderlich. %1$s." "Schlüsselspeicher" - "Der Schlüsselspeicher muss aktiviert sein, um Datenwiederherstellung zu ermöglichen." + "Die Schlüsselspeicherung muss aktiviert sein, damit deine Chats gesichert werden können." "Schlüssel von diesem Gerät hochladen" "Schlüsselspeicherung zulassen" "Wiederherstellungsschlüssel ändern" @@ -34,10 +34,10 @@ "Du musst alle deine bestehenden Geräte und Kontakte erneut verifizieren." "Setze deine digitale Identität nur dann zurück, wenn du keinen Zugriff auf ein anderes verifiziertes Gerät hast und deinen Wiederherstellungsschlüssel verloren hast." "Bestätigung nicht möglich? Setze deine digitale Identität zurück." - "Ausschalten" - "Du verlierst deine verschlüsselten Nachrichten, wenn du auf allen Geräten abgemeldet bist." - "Bist du sicher, dass du das Backup deaktivieren willst?" - "Das Löschen des Schlüsselspeichers entfernt deine kryptografische Identität und deine Nachrichtenschlüssel vom Server. Die folgenden Sicherheitsfunktionen werden deaktiviert:" + "Löschen" + "Wenn du alle deine Geräte entfernst, gehen deine verschlüsselten Chatverläufe verloren und du musst deine digitale Identität zurücksetzen." + "Bist du sicher, dass du den Schlüsselspeicher löschen möchtest?" + "Durch das Löschen des Schlüsselspeichers werden deine digitale Identität und deine Nachrichtenschlüssel vom Server entfernt und die folgenden Sicherheitsfunktionen deaktiviert:" "Kein Nachrichtenverlauf für verschlüsselte Nachrichten auf neuen Geräten" "Kein Zugriff auf verschlüsselten Nachrichten, wenn du überall von %1$s abgemeldet bist" "Möchtest du die Speicherung der Schlüssel wirklich deaktivieren und entfernen?" diff --git a/features/securebackup/impl/src/main/res/values-ja/translations.xml b/features/securebackup/impl/src/main/res/values-ja/translations.xml index 7a7dd041fe..72c8c90809 100644 --- a/features/securebackup/impl/src/main/res/values-ja/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ja/translations.xml @@ -21,7 +21,7 @@ "生成された回復鍵をパスワードマネージャや暗号化に対応するメモアプリに保存してください。" "他の端末を使用して暗号化をリセット" "リセットを続行" - "アカウントの情報と連絡先や設定などは残ります" + "アカウントの情報や連絡先, 設定などは残ります" "サーバー上にのみ存在する過去のメッセージは確認できなくなります" "すべての端末と連絡先を再度検証する必要があります" "デジタルIDのリセットは、他のサインイン済みの端末と、回復鍵の両方へのアクセスを失った場合にのみ行ってください。" 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 1e5d8b03ab..66051ca04b 100644 --- a/features/securebackup/impl/src/main/res/values-pt/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt/translations.xml @@ -2,7 +2,7 @@ "Desativar a cópia de segurança" "Ativar a 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." + "Permite-te ver o teu histórico de mensagens em qualquer dispositivo novo. É necessário para teres cópias de segurança das tuas conversas e da tua identidade digital. %1$s." "Armazenamento de chaves" "O armazenamento de chaves deve ser ativado para configurar a recuperação." "Carrega chaves a partir deste dispositivo" @@ -11,7 +11,7 @@ "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" "O teu armazenamento de chaves está atualmente dessincronizado." - "Configurar recuperação" + "Chave de recuperação" "Abre a %1$s num computador" "Iniciar sessão novamente" "Quando te for pedido para verificares o teu dispositivo, seleciona %1$s" @@ -25,10 +25,10 @@ "Necessitarás de verificar todos os teus dispositivos e contactos novamente." "Repõe a tua identidade apenas se não tiveres acesso a mais nenhum dispositivo com sessão iniciada e se tiveres perdido a tua chave de recuperação." "Repõe a tua identidade caso não consigas confirmar de outra forma" - "Desligar" - "Perderás as tuas mensagens cifradas se tiveres terminado a sessão em todos os teus dispositivos." - "Tens a certeza que queres desativar a cópia de segurança?" - "Desativar a cópia de segurança irá remover a atual cópia da chave de cifragem e desativar outras funcionalidades de segurança. Neste caso, irás:" + "Eliminar" + "Perderás os históricos de conversas cifradas e terás que repor a tua identidade digital caso removas todos os teus dispositivos." + "Tens a certeza que queres eliminar o armazenamento de chaves?" + "Eliminar o armazenamento de chaves irá remover a tua identidade digital e chaves de mensagem do servidor. Irá também desativar as seguintes funcionalidades de segurança:" "Não ter o histórico de mensagens cifradas em novos dispositivos" "Perder o acesso às tuas mensagens cifradas se terminares todas as sessões %1$s" "Tens a certeza que queres desativar a cópia de segurança?" @@ -58,7 +58,7 @@ "Gerar a tua chave de recuperação" "Não partilhes isto com ninguém!" "Recuperação configurada com sucesso" - "Configurar recuperação" + "Chave de recuperação" "Sim, repor agora" "Este processo é irreversível." "Tens a certeza que pretendes repor a tua cifra?" 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 c2e7d5c2cb..459291614e 100644 --- a/features/securebackup/impl/src/main/res/values-zh/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh/translations.xml @@ -48,7 +48,7 @@ "恢复密钥已确认" "输入恢复密钥" "恢复密钥已复制" - "正在生成……" + "正在生成…" "保存恢复密钥" "将此恢复密钥保存在安全的地方,例如密码管理器、加密笔记或物理保险箱。" "点击复制恢复密钥" diff --git a/features/securityandprivacy/impl/src/main/res/values-da/translations.xml b/features/securityandprivacy/impl/src/main/res/values-da/translations.xml index 659799693b..566c26b4af 100644 --- a/features/securityandprivacy/impl/src/main/res/values-da/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-da/translations.xml @@ -29,7 +29,7 @@ Vi anbefaler ikke at aktivere kryptering for rum, som alle kan finde og deltage "Adgang" "Alle i autoriserede klynger kan deltage." "Alle i %1$s kan deltage." - "Medlemmer af rummet" + "Medlemmer af klyngen" "Klynger understøttes ikke i øjeblikket" "Du skal bruge en adresse for at gøre det synligt i det offentlige register." "Adresse" diff --git a/features/securityandprivacy/impl/src/main/res/values-zh/translations.xml b/features/securityandprivacy/impl/src/main/res/values-zh/translations.xml index dda5834ba7..09a22f06b3 100644 --- a/features/securityandprivacy/impl/src/main/res/values-zh/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-zh/translations.xml @@ -24,7 +24,7 @@ "任何人" "选择哪些无需邀请即可加入此房间的空间成员。%1$s" "管理空间" - "仅限受邀者加入。" + "仅限受邀人员加入。" "仅限受邀者" "访问权限" "任何位于已授权空间的成员均可加入。" diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml index f50879c573..2e46202dd4 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -3,7 +3,7 @@ "Bestätigung unmöglich?" "Erstelle einen neuen Wiederherstellungsschlüssel" "Wähle eine Verifizierungsmethode, um den sicheren Nachrichtenversand einzurichten." - "Bestätige deine Identität" + "Bestätige deine digitale Identität" "Ein anderes Gerät verwenden" "Wiederherstellungsschlüssel verwenden" "Du kannst jetzt verschlüsselte Nachrichten lesen und versenden. Dein Chatpartner vertraut nun diesem Gerät." @@ -17,7 +17,7 @@ "Bestätige, dass die folgenden Zahlen mit denen in deiner anderen Sitzung übereinstimmen." "Vergleiche die Zahlen" "Jetzt kannst du verschlüsselte Nachrichten sicher auf deinem anderen Gerät schreiben und lesen." - "Jetzt kannst du der Identität dieses Nutzers vertrauen, wenn du Nachrichten sendest oder empfängst." + "Jetzt kannst du beim Senden oder Empfangen von Nachrichten der digitalen Identität dieses Nutzers vertrauen." "Gerät verifiziert" "Wiederherstellungsschlüssel eingeben" "Entweder ist die Anfrage abgelaufen, oder die Anfrage wurde abgelehnt, oder es gab eine Unstimmigkeit bei der Überprüfung." @@ -42,7 +42,7 @@ "Öffne die App auf einem anderen verifizierten Gerät" "Verifiziere diesen Nutzer für zusätzliche Sicherheit durch den Vergleich einer Reihe von Emojis auf den Geräten. Verwende dazu einen vertraulichen Kommunikationskanal." "Diesen Nutzer verifizieren?" - "Für zusätzliche Sicherheit möchte ein anderer Nutzer deine Identität verifizieren. Es werden dir einige Emojis zum Vergleich angezeigt." + "Zur zusätzlichen Sicherheit möchte ein anderer Nutzer deine digitale Identität verifizieren. Dir werden einige Emojis zum Abgleich angezeigt." "Du solltest ein Popup-Fenster auf dem anderen Gerät sehen. Starte die Verifizierung von dort aus." "Starte die Verifizierung auf dem anderen Gerät" "Starte die Verifizierung auf dem anderen Gerät" diff --git a/features/verifysession/impl/src/main/res/values-fa/translations.xml b/features/verifysession/impl/src/main/res/values-fa/translations.xml index ffc03e1f19..aa902a94e1 100644 --- a/features/verifysession/impl/src/main/res/values-fa/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fa/translations.xml @@ -32,5 +32,5 @@ "مطابقند" "برای ادامه، درخواست شروع فرآیند تأیید را در جلسه دیگر خود بپذیرید." "منظر پذیرش درخواست" - "خارج شدن…" + "برداشتن افزاره…" 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 021a528646..05a5bae0ab 100644 --- a/features/verifysession/impl/src/main/res/values-in/translations.xml +++ b/features/verifysession/impl/src/main/res/values-in/translations.xml @@ -50,5 +50,5 @@ "Setelah diterima, Anda akan dapat melanjutkan verifikasi." "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan." "Menunggu untuk menerima permintaan" - "Mengeluarkan dari akun…" + "Mengeluarkan device dari akun…" 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 bf32d3c7b9..2bbda1497a 100644 --- a/features/verifysession/impl/src/main/res/values-pt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt/translations.xml @@ -3,7 +3,7 @@ "Não é possível confirmar?" "Criar uma nova chave de recuperação" "Verifica este dispositivo para configurar o envio seguro de mensagens." - "Confirma que és tu" + "Confirma a tua identidade digital" "Utilizar outro dispositivo" "Utilizar chave de recuperação" "Agora podes ler ou enviar mensagens de forma segura, e qualquer pessoa com quem converses também pode confiar neste dispositivo." @@ -50,5 +50,5 @@ "Uma vez aceite, poderás continuar com a verificação." "Para continuar, aceita o pedido de verificação na tua outra sessão." "À aguardar a aceitação do pedido" - "A terminar sessão…" + "A remover dispositivo…" diff --git a/features/verifysession/impl/src/main/res/values-zh/translations.xml b/features/verifysession/impl/src/main/res/values-zh/translations.xml index 63cb67330b..61b7b078dd 100644 --- a/features/verifysession/impl/src/main/res/values-zh/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh/translations.xml @@ -9,7 +9,7 @@ "现在你可以安全地读取或发送消息,并且与你聊天的任何人也可以信任此设备。" "设备已验证" "使用其它设备" - "正在等待其它设备……" + "正在等待其它设备…" "发生了一些错误。网络请求超时,或者被服务器拒绝。" "确认以下 Emoji 与你的其它设备上显示的相匹配。" "比较 Emoji" @@ -25,7 +25,7 @@ "打开已有会话" "重试验证" "准备就绪" - "等待比对……" + "正在等待对比…" "比较一组唯一的 Emoji。" "比较唯一的 Emoji,确保它们以相同顺序排列。" "已登录" @@ -50,5 +50,5 @@ "一旦被接受,你将能够继续进行验证。" "接受此请求以在另一会话中开始验证流程以继续操作。" "等待接受请求" - "正在删除设备……" + "正在移除设备…" diff --git a/libraries/dateformatter/impl/src/main/res/values-zh/translations.xml b/libraries/dateformatter/impl/src/main/res/values-zh/translations.xml index 9fab311a1d..d1ee550b73 100644 --- a/libraries/dateformatter/impl/src/main/res/values-zh/translations.xml +++ b/libraries/dateformatter/impl/src/main/res/values-zh/translations.xml @@ -1,5 +1,5 @@ - "%1$s在 %2$s" + "于 %1$s %2$s" "本月" diff --git a/libraries/eventformatter/impl/src/main/res/values-zh/translations.xml b/libraries/eventformatter/impl/src/main/res/values-zh/translations.xml index 35a501c7a2..f7e5d7d7f2 100644 --- a/libraries/eventformatter/impl/src/main/res/values-zh/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-zh/translations.xml @@ -45,8 +45,8 @@ "你将房间名称更改为 %1$s" "%1$s 移除了房间名称" "你移除了房间名称" - "%1$s 没有任何更改" - "你未做任何更改" + "%1$s 未产生任何更改" + "你未产生任何更改" "%1$s 更改了已置顶的消息" "你更改了已置顶的消息" "%1$s 置顶了 1 个消息" diff --git a/libraries/matrixui/src/main/res/values-da/translations.xml b/libraries/matrixui/src/main/res/values-da/translations.xml index 5b4970f003..f2dfa27073 100644 --- a/libraries/matrixui/src/main/res/values-da/translations.xml +++ b/libraries/matrixui/src/main/res/values-da/translations.xml @@ -3,5 +3,7 @@ "Send invitation" "Kunne du tænke dig at starte en samtale med %1$s?" "Send invitation?" + "Du har i øjeblikket ingen samtaler med denne person. Bekræft invitationen, før du fortsætter." + "Vil du starte en chat med denne nye kontakt?" "%1$s(%2$s ) inviterede dig" diff --git a/libraries/matrixui/src/main/res/values-zh/translations.xml b/libraries/matrixui/src/main/res/values-zh/translations.xml index b466f13226..e05e21095d 100644 --- a/libraries/matrixui/src/main/res/values-zh/translations.xml +++ b/libraries/matrixui/src/main/res/values-zh/translations.xml @@ -5,5 +5,5 @@ "发送邀请?" "你与此人暂无任何聊天。请确认对方被邀请后再继续。" "是否与新联系人开始聊天?" - "%1$s (%2$s)邀请了你" + "%1$s(%2$s)邀请了你" diff --git a/libraries/mediaviewer/impl/src/main/res/values-da/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-da/translations.xml index 5d88458495..6108eab22d 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-da/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-da/translations.xml @@ -16,6 +16,7 @@ "Filnavn" "Ikke flere filer at vise" "Ikke flere medier at vise" + "Filoplysninger" "Uploadet af" "Uploadet på" diff --git a/libraries/mediaviewer/impl/src/main/res/values-fr/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-fr/translations.xml index b6334095ab..77372c1374 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-fr/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-fr/translations.xml @@ -16,6 +16,7 @@ "Nom du fichier" "Il n’y a plus de fichiers à montrer" "Il n’y a plus de médias à montrer" + "Informations sur le fichier" "Envoyé par" "Envoyé le" diff --git a/libraries/mediaviewer/impl/src/main/res/values-hr/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-hr/translations.xml index 7637fbf350..2a417cc070 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-hr/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-hr/translations.xml @@ -16,6 +16,7 @@ "Naziv datoteke" "Nema više datoteka za prikaz" "Nema više medijskih sadržaja za prikaz" + "Informacije o datoteci" "Prenio/la" "Preneseno na" diff --git a/libraries/mediaviewer/impl/src/main/res/values-hu/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-hu/translations.xml index 14cc11fb96..4f4d244d44 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-hu/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-hu/translations.xml @@ -16,6 +16,7 @@ "Fájlnév" "Nincs több megjeleníthető fájl" "Nincs több megjeleníthető média" + "Fájlinformáció" "Feltöltötte:" "Feltöltve:" diff --git a/libraries/mediaviewer/impl/src/main/res/values-ja/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ja/translations.xml index cb4da33df8..9e136b0884 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-ja/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-ja/translations.xml @@ -16,6 +16,7 @@ "ファイル名" "これ以上ファイルはありません" "これ以上メディアはありません" + "ファイル情報" "アップロード元" "アップロード先" diff --git a/libraries/push/impl/src/main/res/values-zh/translations.xml b/libraries/push/impl/src/main/res/values-zh/translations.xml index 21988770c0..881dbcdc54 100644 --- a/libraries/push/impl/src/main/res/values-zh/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh/translations.xml @@ -14,7 +14,7 @@ "UnifiedPush 分发器注册失败,你将无法再接收通知。请检查 app 的通知设置及推送分发器的状态。" "你有新消息。" - "你有 %d 条新消息。" + "你有 %d 个新消息。" "📞 来电" "📹 来电" diff --git a/libraries/textcomposer/impl/src/main/res/values-zh/translations.xml b/libraries/textcomposer/impl/src/main/res/values-zh/translations.xml index fdde227bef..f79cb2f30c 100644 --- a/libraries/textcomposer/impl/src/main/res/values-zh/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-zh/translations.xml @@ -4,8 +4,8 @@ "切换符号列表" "取消并关闭文本格式" "切换代码块" - "可选的标题……" - "加密信息…" + "添加标题…" + "已加密的消息…" "消息…" "未加密的消息…" "创建链接" diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index e9ed8edaa4..f1ff7ec747 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -14,9 +14,10 @@ "Krypteringsoplysninger" "Udvid tekstfeltet for beskeder" "Skjul adgangskode" + "Info" "Deltag i opkald" "Hop til bunden" - "Flyt kortet til min lokation" + "Flyt kortet til min placering" "Kun omtaler" "Lyd slået fra" "Nye omtaler" @@ -48,8 +49,10 @@ "Send filer" "Afsenderens placering" "Tidsbegrænset handling påkrævet, du har et minut til at bekræfte" + "Indstillinger, handling påkrævet" "Vis adgangskode" "Start et opkald" + "Start et videoopkald" "Start et taleopkald" "Deaktiveret rum" "Avatar for bruger" @@ -87,12 +90,15 @@ "Deaktiver konto" "Afvis" "Afvis og blokér" + "Slet" + "Slet konto" "Slet afstemning" "Fravælg alle" "Deaktiver" "Kassér" "Afvis" "Færdig" + "Hent" "Redigér" "Rediger billedtekst" "Redigér afstemning" @@ -160,7 +166,7 @@ "Send talebesked" "Del" "Del link" - "Del liveplacering" + "Del placering live" "Vis" "Log ind igen" "Fjern denne enhed" @@ -200,7 +206,9 @@ "Beta" "Blokerede brugere" "Bobler" + "Opkald afvist" "Opkald startet" + "Du afviste et opkald" "Backup af samtale" "Kopieret til udklipsholder" "Ophavsret" @@ -357,7 +365,7 @@ "Noget gik galt" "Vi stødte på et problem. Prøv venligst igen." "Klynge" - "Medlemmer af rummet" + "Medlemmer af klyngen" "Hvad handler denne klynge om?" "%1$d Klynge" @@ -465,6 +473,13 @@ Er du sikker på, at du vil fortsætte?" "Valgmuligheder" "Fjern %1$s" "Indstillinger" + "Ingen deler deres placering" + "Deler placering live" + + "%1$d person" + "%1$d personer" + + "På kortet" "Det lykkedes ikke at vælge medie. Prøv igen." "Tryk på en besked og vælg \"%1$s\" for at inkludere den her." "Fastgør vigtige beskeder, så de let kan opdages" @@ -489,14 +504,15 @@ Er du sikker på, at du vil fortsætte?" "Besked i %1$s" "Udvid" "Reducér" + "Deler placering live" "Du ser allerede dette rum!" "%1$s af %2$s" "%1$s Fastgjorte beskeder" "Indlæser besked…" "Se alle" "Samtale" - "Del lokation" - "Del min lokation" + "Del placering" + "Del min placering" "Åbn i Apple Maps" "Åbn i Google Maps" "Åbn i OpenStreetMap" @@ -512,7 +528,7 @@ Er du sikker på, at du vil fortsætte?" "Beskeden blev ikke sendt fordi %1$s s bekræftede digitale identitet blev nulstillet." "Meddelelsen er ikke sendt, fordi %1$s ikke har bekræftet alle enheder." "Beskeden er ikke sendt, fordi du ikke har verificeret en eller flere af dine enheder." - "Lokation" + "Placering" "Version: %1$s (%2$s)" "da" "Historiske beskeder er ikke tilgængelige på denne enhed" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 81cb12eb78..739d003f88 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -447,6 +447,7 @@ Möchtest du wirklich fortfahren?" "%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut." "Fehler beim Hochladen der Sprachnachricht." "Der Chat existiert nicht mehr oder die Einladung ist nicht mehr gültig." + "Bitte aktiviere dein GPS, um auf standortbezogene Funktionen zugreifen zu können." "Nachricht nicht gefunden" "%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Du kannst den Zugriff in den Einstellungen aktivieren." "%1$s hat keine Berechtigung, auf deinen Standort zuzugreifen. Erlaube unten den Zugriff." @@ -473,7 +474,7 @@ Möchtest du wirklich fortfahren?" "%1$d fixierte Nachrichten" "Fixierte Nachrichten" - "Du wirst jetzt zu deinem %1$s Konto geleitet, um deine Identität zurückzusetzen. Danach wirst du zur App zurückgebracht." + "Du wirst gleich zu deinem %1$s Konto weitergeleitet, um deine digitale Identität zurückzusetzen. Danach kehrst du zur App zurück." "Bestätigung nicht möglich? Rufe dein Konto auf, um deine digitale Identität zurückzusetzen." "Verifizierung zurückziehen und senden" "Du kannst deine Verifizierung zurückziehen und diese Nachricht trotzdem senden, oder du kannst vorerst abbrechen und es später noch einmal versuchen, nachdem du %1$s erneut verifiziert hast." @@ -509,7 +510,7 @@ Möchtest du wirklich fortfahren?" "Spaces" "Geteilt %1$s" "Auf der Karte" - "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$s geändert hat." + "Die Nachricht wurde nicht gesendet, da die verifizierte digitale Identität von %1$s zurückgesetzt wurde." "Die Nachricht wurde nicht gesendet, weil %1$s nicht alle Geräte verifiziert hat." "Die Nachricht wurde nicht gesendet, weil du eines oder mehrere deiner Geräte nicht verifiziert hast." "Standort" @@ -519,5 +520,5 @@ Möchtest du wirklich fortfahren?" "Für den Zugriff auf den Nachrichtenverlauf musst du dieses Gerät verifizieren" "Du hast keinen Zugriff auf diese Nachricht." "Nachricht kann nicht entschlüsselt werden" - "Diese Nachricht wurde entweder blockiert, weil du dein Gerät nicht verifiziert hast oder weil der Absender deine Identität verifizieren muss." + "Diese Nachricht wurde entweder blockiert, weil du dein Gerät nicht verifiziert hast, oder weil der Absender deine digitale Identität verifizieren muss." diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index fbe716bc49..872493f7de 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -145,7 +145,7 @@ "هم‌رسانی پیوند" "نمایش" "ورود دوباره" - "خروج" + "برداشتن این افزاره" "خروج به هر صورت" "پرش" "آغاز" @@ -389,7 +389,7 @@ "گشودن در نقشه‌های اپل" "گشودن در نقشه‌های گوگل" "گشودن در اوپن‌استریت‌مپ" - "هم‌رسانی این مکان" + "هم‌رسانی مکان گزیده" "فضاهایی که ساخته یا پیوسته‌اید." "%1$s • %2$s" "‏%1$s فضا" 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 08791b1b38..7e0c4e9481 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -14,6 +14,7 @@ "Détails du chiffrement" "Augmenter la taille du composeur" "Masquer le mot de passe" + "Info" "Rejoindre l’appel" "Retourner à la fin de la conversation" "Déplacer la carte vers ma position" @@ -48,6 +49,7 @@ "Envoyer des fichiers" "Position de l’expéditeur" "Action limitée dans le temps requise, vous avez une minute pour effectuer la vérification" + "Paramètres, action requise" "Afficher le mot de passe" "Démarrer un appel" "Passer un appel vidéo" @@ -96,6 +98,7 @@ "Annuler" "Ignorer" "Terminé" + "Télécharger" "Modifier" "Modifier la légende" "Modifier le sondage" @@ -203,7 +206,9 @@ "Bêta" "Utilisateurs bloqués" "Bulles" + "Appel rejeté" "Appel démarré" + "Vous avez rejeté un appel" "Sauvegarde des discussions" "Copié dans le presse-papiers" "Droits d’auteur" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index 61c9ea54e2..2382eafc1a 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -15,6 +15,7 @@ "Pojedinosti o šifriranju" "Proširi tekstno polje poruke" "Sakrij zaporku" + "Informacije" "Pridruži se pozivu" "Idi na dno" "Pomakni kartu na moju lokaciju" @@ -50,6 +51,7 @@ "Pošalji datoteke" "Lokacija pošiljatelja" "Potrebna je vremenski ograničena radnja, imate jednu minutu za potvrdu" + "Postavke, potrebna je radnja" "Prikaži zaporku" "Započni poziv" "Započni videopoziv" @@ -90,12 +92,15 @@ "Deaktiviraj račun" "Odbij" "Odbij i blokiraj" + "Izbriši" + "Izbriši račun" "Izbriši anketu" "Poništi sve odabire" "Onemogući" "Odbaci" "Odbaci" "Gotovo" + "Preuzmi" "Uredi" "Uredi opis" "Uredi anketu" @@ -203,7 +208,9 @@ "Beta" "Blokirani korisnici" "Mjehurići" + "Poziv je odbijen" "Poziv je započeo" + "Odbili ste poziv" "Sigurnosna kopija razgovora" "Kopirano u međuspremnik" "Autorsko pravo" @@ -475,6 +482,14 @@ Jeste li sigurni da želite nastaviti?" "Mogućnosti" "Ukloni %1$s" "Postavke" + "Nitko ne dijeli svoju lokaciju" + "Dijeljenje lokacije uživo" + + "%1$d osoba" + "%1$d osoba" + "%1$d ljudi" + + "Na karti" "Odabir medija nije uspio, pokušajte ponovno." "Pritisnite poruku i odaberite “%1$s” kako biste uključili ovdje." "Prikvačite važne poruke kako bi ih se lakše moglo pronaći" 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 06f49eba93..f6498f228f 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -14,6 +14,7 @@ "Titkosítás részletei" "Üzenet szövegmezőjének kibontása" "Jelszó elrejtése" + "Információ" "Csatlakozás a híváshoz" "Ugrás az aljára" "Térkép áthelyezése a jelenlegi helyre" @@ -48,6 +49,7 @@ "Fájlküldés" "Felhasználó tartózkodási helye" "Időkorlátos művelet szükséges, egy perce van az ellenőrzésre" + "Beállítások, beavatkozás szükséges" "Jelszó megjelenítése" "Hanghívás indítása" "Videohívás indítása" @@ -88,12 +90,15 @@ "Fiók deaktiválása" "Elutasítás" "Elutasítás és letiltás" + "Törlés" + "Fiók törlése" "Szavazás törlése" "Kijelölés megszüntetése" "Letiltás" "Elvetés" "Eltüntetés" "Kész" + "Letöltés" "Szerkesztés" "Felirat szerkesztése" "Szavazás szerkesztése" @@ -201,7 +206,9 @@ "Béta" "Letiltott felhasználók" "Buborékok" + "Hívás elutasítva" "A hívás elindult" + "Elutasított egy hívást" "Csevegés biztonsági mentése" "A vágólapra másolva" "Szerzői jogok" 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 5a095a074f..77e405e1c8 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -144,7 +144,7 @@ "Bagikan tautan" "Tampilkan" "Masuk lagi" - "Keluar dari akun" + "Hapus device dari akun" "Keluar saja" "Lewati" "Mulai" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index ae5592d3f6..1c022f850f 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -13,6 +13,7 @@ "暗号化の詳細" "入力欄を拡大" "パスワードを非表示" + "情報" "通話に参加" "一番下へ" "現在地に移動" @@ -46,6 +47,7 @@ "ファイルを送信" "送信者の位置情報" "1分以内に検証を完了してください" + "処置が必要な設定" "パスワードを表示" "通話を開始" "ビデオ通話を開始" @@ -67,6 +69,7 @@ "通話" "キャンセル" "今回のみキャンセル" + "ファイルを選択" "写真を選択" "クリア" "閉じる" @@ -94,6 +97,7 @@ "破棄" "無視" "完了" + "ダウンロード" "編集" "キャプションを編集" "投票を編集" @@ -196,12 +200,14 @@ "通知を同期中…" "あなたがルームを退出" "セッションからログアウトされました" - "外観" + "テーマ" "音声" "ベータ版" "ブロックしたユーザー" "ふきだし" + "通話を拒否しました" "通話を開始しました" + "通話を拒否しました" "チャットをバックアップ" "クリップボードにコピーしました" "著作権" 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 56346fe1ac..dd15875590 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -152,8 +152,8 @@ "Partilhar ligação" "Mostrar" "Iniciar sessão novamente" - "Terminar sessão" - "Terminar mesmo assim" + "Remover este dispositivo" + "Remover mesmo assim" "Saltar" "Iniciar" "Iniciar conversa" @@ -322,7 +322,7 @@ Razão: %1$s." "Partilhar espaço" "Localização partilhada" "Espaço partilhado" - "A terminar sessão" + "A remover dispositivo" "Algo correu mal" "Encontramos um erro. Por favor, tenta novamente." "Espaço" @@ -458,7 +458,7 @@ Tens a certeza de que queres continuar?" "Abrir no Apple Maps" "Abrir no Google Maps" "Abrir no OpenStreetMap" - "Partilhar este local" + "Partilhar local selecionado" "Espaços que criaste ou nos quais entraste." "%1$s • %2$s" "Espaço %1$s" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 7bd218ca54..974e91b530 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -40,7 +40,7 @@ "%1$s 已读" "点击以显示全部" - "撤回反应 %1$s" + "移除反应:%1$s" "移除反应:%1$s" "房间头像" "发送文件" @@ -110,9 +110,9 @@ "前往设置" "忽略" "邀请" - "邀请朋友" - "邀请别人加入 %1$s" - "邀请别人加入 %1$s" + "邀请人员" + "邀请人员加入 %1$s" + "邀请人员加入 %1$s" "邀请" "加入" "了解更多" @@ -145,7 +145,7 @@ "回复" "在消息列中回复" "举报" - "报告错误" + "报告 bug" "举报内容" "举报对话" "举报房间" @@ -165,8 +165,8 @@ "共享实时位置" "显示" "再次登录" - "删除此设备" - "仍要删除此设备" + "移除此设备" + "仍要移除此设备" "跳过" "开始" "开始聊天" @@ -238,7 +238,7 @@ 原因:%1$s。" "所有人" "失败" - "收藏夹" + "收藏" "已收藏" "文件" "文件已删除" @@ -285,7 +285,7 @@ "或" "其它选项" "密码" - "用户" + "人员" "永久链接" "权限" "已置顶" @@ -408,7 +408,7 @@ "由于你当时不在房间内,%1$s(%2$s)已将消息向你共享。" "由于你当时不在房间内,%1$s 已将消息向你共享。" "此房间已配置为允许新成员阅读历史。%1$s" - "%1$s的数字身份已重置。%2$s" + "%1$s 的数字身份已重置。%2$s" "%1$s %2$s 的数字身份已重置。%3$s" "(%1$s)" "%1$s 的数字身份已重置。" @@ -440,7 +440,7 @@ "你的主服务器需要升级,以支持 Matrix 认证服务和账户创建。" "永久链接创建失败" "%1$s 无法加载地图,请稍后再试。" - "加载消息失败" + "消息加载失败" "%1$s 无法访问你的位置,请稍后再试。" "无法上传语音消息。" "该房间已不存在或邀请已失效。" @@ -495,7 +495,7 @@ "共享实时位置" "已经位于此房间!" "%1$s / %2$s" - "置顶消息 %1$s" + "%1$s 个已置顶的消息" "正在加载消息…" "查看全部" "聊天" @@ -511,9 +511,9 @@ "创建空间以组织房间" "空间 %1$s" "空间" - "共享于%1$s" + "已共享 %1$s" "在地图上" - "消息未发送,因为%1$s的已验证数字身份已被重置。" + "消息未能发送,因为 %1$s 的已验证数字身份已被重置。" "消息未能发送,因为 %1$s 尚未验证所有设备。" "消息未能发送,因为你有尚未验证的设备。" "位置" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 9db9bb4751..cdb3c52e6a 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -71,6 +71,7 @@ "Call" "Cancel" "Cancel for now" + "Choose file" "Choose photo" "Clear" "Close" diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png index 8843434a9f..08a4e04bba 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e31bebf55244a497eaf3442701b0db8b5ee6c061944c1b4e72da487660104643 -size 40044 +oid sha256:9b6138083429a82d1de576009df367a52361b39e5ac094d0c4abc0bb47e89550 +size 41888 diff --git a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png index 4e4cdee57b..5771464dd2 100644 --- a/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_ConfigureRoomViewLight_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ce67e2cd809b3ba7f32c57cf87208e71a92df71b545bea2554ed7f6d28e78e7 -size 41381 +oid sha256:981e3075d5285a630932c02427cca3d46efad74a640f12d35da8e0f29fb4b779 +size 43474 diff --git a/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png b/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png index 21c6405a77..1cc92bddf4 100644 --- a/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png +++ b/screenshots/de/features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e0fb8867a616d7d575f9262996f66730cb61f8b0e9c2c74fffbb92b3a1fd06 -size 25852 +oid sha256:ac79f05488afb65d5675a8068ac1c70c7cddc81b1db5445f1314ce53a1da8ba1 +size 27143 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png index fdbc13a1ee..5edce6541b 100644 --- a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b62cf320453eb36be6e486e3100c3be1420fcdfd068f2276d27a824fb84e3f05 -size 42064 +oid sha256:9c1602858c58d4f05c5c3d816cde905af8cd888ae9337215119902b0dbdf1cbc +size 43609 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png index ddbdfb051f..641671bf3f 100644 --- a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88633b6deed6e44e354cf3c8ac9e8553f0dd41743198cf5d8ff57de51ecd45e4 -size 35217 +oid sha256:1acc89193300b0ea268c127c98c7d46b8e23841933750d99d597a8713e1698ed +size 36762 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png index 0dafa02950..b9ce42a450 100644 --- a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0887222f914448055346fd314b3a8bfdaeda5262b76f509749ec01877728a821 -size 48344 +oid sha256:0f5bddbb812e5fd0863a8c1308349f627f0e8fd865ee36f995a29666e0816b0d +size 49843 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png index 8051720f82..f8be736047 100644 --- a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c355fb7f9e899e9cc7fe9a4a0aefacb7f57e669b60927b9c77f29630a6168b0 -size 41774 +oid sha256:5e141bd8ca4767ded168a1e7cb574df8d33797e05ced6615287469b282d985b1 +size 43319 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_de.png index 40197c311f..5697209279 100644 --- a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_de.png +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6085e75ee0b5669b078b7ff40c6f9e91f04263967a46076dda95802992224318 -size 31603 +oid sha256:15bba5407bbda9070cf2109165e4fb9455450c8d6dd504dea7b521640fcfa48d +size 33229 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png new file mode 100644 index 0000000000..efd83f7af8 --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2e4ee3ec519388e7acdedac96636aa3f0e0d9dc4f4a061a0b3e4c345b824a3d +size 24961 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png new file mode 100644 index 0000000000..b7a581021f --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d367fc2d1c620eb370e28db890e4f01acd85b0273b241d0b0b701f23939fabc8 +size 25377 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png new file mode 100644 index 0000000000..0c1e7996ff --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6175b377fcdb3f70739c36ceb1d59a7e108d8f1d08b4c10ec6d28744ff0f0545 +size 27442 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_de.png deleted file mode 100644 index b51bbdbfc7..0000000000 --- a/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf13adc113fe7b1127eedde7ed659ac11afb25b25be22f757dfd22c363e011f5 -size 29190 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_de.png new file mode 100644 index 0000000000..67b0361c5c --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26a5736c3ce48028bde69a7f029ffaed5d18e490b32fbb455d35d89f90f7e169 +size 30385 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_de.png b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_de.png new file mode 100644 index 0000000000..c39d87d73c --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9caedf891866305599b0cc1887f46fc2ae7e0c6ca903860df247eb48eadf3708 +size 45796 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_de.png b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_de.png new file mode 100644 index 0000000000..950a33e731 --- /dev/null +++ b/screenshots/de/features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:469c3186032dae6ed8d4203277b9a45d5c5f648e7688a5a6804231ac648daef4 +size 30940 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_de.png deleted file mode 100644 index 097191f4f2..0000000000 --- a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba628bba3efe736cbfd164d8c5a403ef042cefd29fef6307fc2c2694798b0e18 -size 23407 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_de.png b/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_de.png deleted file mode 100644 index d9912fb3e0..0000000000 --- a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:811bc09258019248f5cd94ee8cfeb070b1471f510ba4ad16b427e4c574db46a9 -size 23660 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_de.png b/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_de.png deleted file mode 100644 index cc6b3c7a57..0000000000 --- a/screenshots/de/features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7dada5eb605d5558baea8b01101bfbb8cc31338b2ce76035a8fcb449cdd8e77b -size 25776 diff --git a/screenshots/de/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_de.png b/screenshots/de/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_de.png new file mode 100644 index 0000000000..630aee04c5 --- /dev/null +++ b/screenshots/de/features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:495afd76bb17398747975e89e5dadb28e7b8c20309725264be293420b8518ec8 +size 38456 diff --git a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_2_de.png b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_2_de.png index 92ffd71139..308abe5a72 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_2_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf9aea8bd7c9f6d7eb7f31a71ec412ae0f143c60bc6834a1f2ad99e538d87875 -size 33758 +oid sha256:a1e6cd137958c6f3cd9ddad7cdf9dcd35c9b9963cdab164c232ebefa50b7a81b +size 33815 diff --git a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png index 0f07d0568c..7ea7736a31 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:968c091419c902a4c135a4498ebe2422566d0f2dac72882e81ac5dc6572a9e77 -size 42859 +oid sha256:06fa1d9cfcdd7e81dd5d08f584651d982136e7491041b4db6c0feb9875c2db49 +size 43509 diff --git a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_4_de.png b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_4_de.png index feacf82d89..0b411260ec 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_4_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e71f2781efd689a0c57613cf03fec734474128ae444b8079d6124f8b9aa19fb -size 45605 +oid sha256:0b41b171588b46f2b17fe366d311da43653c43dc1eccf539c7f02c12d1c7a21f +size 45688 diff --git a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_8_de.png b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_8_de.png new file mode 100644 index 0000000000..cba542a991 --- /dev/null +++ b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e77e5930c00c3389179fc5a60a6a2d173223adaf742a53b6900ab96ab0af88a +size 22202 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_de.png index 1ea459175f..0e9606c453 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91215fa6820bbe0d6da772de2720cde77bc731f28954d067c6a6a0d95709f624 -size 19238 +oid sha256:748da581859b8b1a05a2b5703c17b31f6725d26713053063dc825b425b4d4131 +size 19317 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_de.png index 45ed4366dc..7127f05f27 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:419f4edd8b097ca40f68d5012c5b0c0e6c1301b6efcd01b13ce0c8e7a13996c6 -size 25430 +oid sha256:446b2dbace2a10eb7332886ce7fa83f992c5314f3a66fd402a13200e9f0f06b2 +size 25489 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_de.png index a9af23935f..c5645b1b44 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e40215a3f06166fa78319875ca074876e41e19a9155794f4d0b68a132045a276 -size 27975 +oid sha256:84da7365709446471e67dedd9f733786989f56fa1d3fe83ea5ec02344076983f +size 28966 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_de.png index e172ea6f3d..f466ac0135 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1684330a9b215bb22fbefe2e59d456c9cbc31e75e01828c67a256b0a02b667f -size 25956 +oid sha256:a7b92adb8584cf31853a01bda52aacab97df832360399adbf63980ebb612ce76 +size 26029 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_de.png index 4b15ed217c..2fdab6284a 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e96f99f81040aa3214a1acd937d25299d080ca0191258562cb08f78fbdeff7e -size 26188 +oid sha256:cde54cd972be78cb89278b4382d7b71297bdeddb0a16503211ed0342ad059a2b +size 26237 diff --git a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_de.png b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_de.png index 75f85cba2e..fbda7d633d 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:759da98dca2f26b5a7e87954fceba866e7fb526dc36587b62cb3d01f3ba29b53 -size 34992 +oid sha256:76c3bacabeab51017a076a58ff61176ec45fff9711b86471cf1e6752692a201a +size 35056 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png index f4290dda6e..8381a894f4 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2eb237f3bea51645310fe28e66a374ee7eea722d262261faf7a2d4ad7bfc9515 -size 30400 +oid sha256:6ad541415245d6e6ca95514db2aa053691d8d2c8193cf0268be67cc74b1af4ea +size 32928 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_5_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_5_de.png index 8cf129ffae..6a87af5d60 100644 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_5_de.png +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ad009477e6df04362db43d17c2a43599ae19812ef4244b0ffdd436e18662969 -size 32383 +oid sha256:da8a5e9159c4817b5c500d721e378e51d1f3ed6bea5468b54e4e66db1969c292 +size 34873 diff --git a/screenshots/de/features.login.impl.changeserver_ChangeServerView_Day_5_de.png b/screenshots/de/features.login.impl.changeserver_ChangeServerView_Day_5_de.png index c6a8d72619..ec9de6ee4e 100644 --- a/screenshots/de/features.login.impl.changeserver_ChangeServerView_Day_5_de.png +++ b/screenshots/de/features.login.impl.changeserver_ChangeServerView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c3b505df79be2829a102e9a0249fc23c5f5de1e7fa19570a1fd1e5ab7f9aaee -size 30819 +oid sha256:228c78854193cffdf1a0a22a80eb742b01dcef195192196f7b63c32f3d0666e8 +size 30823 diff --git a/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png b/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png index c6a8d72619..ec9de6ee4e 100644 --- a/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png +++ b/screenshots/de/features.login.impl.login_LoginModeView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c3b505df79be2829a102e9a0249fc23c5f5de1e7fa19570a1fd1e5ab7f9aaee -size 30819 +oid sha256:228c78854193cffdf1a0a22a80eb742b01dcef195192196f7b63c32f3d0666e8 +size 30823 diff --git a/screenshots/de/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_de.png b/screenshots/de/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_de.png index 50663c9d70..7848b3de7f 100644 --- a/screenshots/de/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_de.png +++ b/screenshots/de/features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8153b9a20bb2cda60b59abb617f527b165f54fee15235384b580de74b2ae31aa -size 37912 +oid sha256:7ead8637651c4d0e3c0f6e78c596c4781d00ecb493bdf54ac96670b55ff541c2 +size 38217 diff --git a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_de.png b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_de.png index 92ffd71139..308abe5a72 100644 --- a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_de.png +++ b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf9aea8bd7c9f6d7eb7f31a71ec412ae0f143c60bc6834a1f2ad99e538d87875 -size 33758 +oid sha256:a1e6cd137958c6f3cd9ddad7cdf9dcd35c9b9963cdab164c232ebefa50b7a81b +size 33815 diff --git a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_de.png b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_de.png index 3e1a99aec0..da94eb9d22 100644 --- a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_de.png +++ b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de35cb70fa0076e5c1d0ab35cdae0b48cac90aa0c0223538276054e69768fd2a -size 42776 +oid sha256:3eeb6eb4ff66c0558d2abc960a8bffc757d6a8fd043f9c4eee4667176de38617 +size 43485 diff --git a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_de.png b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_de.png index 11011887f3..49aa77efd2 100644 --- a/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_de.png +++ b/screenshots/de/features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a4508363f01ad58b30503ff92236349b619feaa5716524438a2f52f033d71e6 -size 26225 +oid sha256:e1c0c74a437c19c02f04c6de9898fc8fcce98ebdd0161f97d9973d6bf091db58 +size 27056 diff --git a/screenshots/de/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_de.png b/screenshots/de/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_de.png index e55f0530fd..4119ae071b 100644 --- a/screenshots/de/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_de.png +++ b/screenshots/de/features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f27bdc5b6fc21700a4f8512230084f1131d9804eb7cd4366d61f90deaf55bb60 -size 56835 +oid sha256:b742d5311fc1961bc9a5a61658e77fdf1ab3f83b5fb93e0a4fedd7f50a3faacb +size 56999 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png index 38bcc092a9..c19097e89a 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21e1cce3efb6c835a83b1a0c29dc1aadbcf6d5a4c2e839343f71fa30ea6dd0c4 -size 91151 +oid sha256:392e090f14a98249b1cf3cf4acf180a817c41dbc8b4320ad5b8a2b4636ba0f14 +size 86883 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png index 4fe8b578bd..c84f36df47 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be7af996c56f6c75636688d88c70814dd7c93e3963255d6dc2e89fc8032a51c5 -size 90886 +oid sha256:2488a2d453b8a8cd086e531e1c49ac2d41186713b85c1aba3df73cf6c7a5f1e8 +size 86605 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png index e22e792f54..8dd9a8233d 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7901b1eb373bfac3fa7a9549c66c157b0d70cd885364646fa37e014056d0bcd8 -size 78805 +oid sha256:f0870cc34ef448fc914f7f4a688781bacaca59edad593509ee5cb333fdcf4acd +size 73713 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png index c872795d91..50752acb56 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40745997037a4b3856628bc8d485c8131f4586e0f855513fdfe97b2470507bc4 -size 70087 +oid sha256:844f24409611ccc30b514427c72cd057ed838a1b4ba058a4c6dd941858908e65 +size 66529 diff --git a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png index 198287f424..e20e0c63c8 100644 --- a/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png +++ b/screenshots/de/features.logout.impl_AccountDeactivationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3471dcaa2c715b6c3fb8c6b5223f1cb398af8d9b7ff013af11f4c6feef7d4615 -size 61333 +oid sha256:cf40d60799deb62643ca650deb1a5b5e18f5d26ebca7927064c51a0f3a8b771a +size 57890 diff --git a/screenshots/de/features.logout.impl_LogoutView_Day_1_de.png b/screenshots/de/features.logout.impl_LogoutView_Day_1_de.png index ba6edcd46e..7354b5bf41 100644 --- a/screenshots/de/features.logout.impl_LogoutView_Day_1_de.png +++ b/screenshots/de/features.logout.impl_LogoutView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f86f77e8055c5d568e4f1e3bf1757a140fef5839eadd8f17731fc079a0c08bbf -size 58945 +oid sha256:08fa6f2925434cc451adb7296041af54941b7b9e126887bd529692fa208060ae +size 69544 diff --git a/screenshots/de/features.logout.impl_LogoutView_Day_3_de.png b/screenshots/de/features.logout.impl_LogoutView_Day_3_de.png index ba6edcd46e..7354b5bf41 100644 --- a/screenshots/de/features.logout.impl_LogoutView_Day_3_de.png +++ b/screenshots/de/features.logout.impl_LogoutView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f86f77e8055c5d568e4f1e3bf1757a140fef5839eadd8f17731fc079a0c08bbf -size 58945 +oid sha256:08fa6f2925434cc451adb7296041af54941b7b9e126887bd529692fa208060ae +size 69544 diff --git a/screenshots/de/features.logout.impl_LogoutView_Day_7_de.png b/screenshots/de/features.logout.impl_LogoutView_Day_7_de.png index 7704df7d91..ed1172e6ae 100644 --- a/screenshots/de/features.logout.impl_LogoutView_Day_7_de.png +++ b/screenshots/de/features.logout.impl_LogoutView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a56b1ee1bde9ad25090371050081ce13c900d6d085c21b63abd858229fe482d -size 42706 +oid sha256:fadcc1a9018cca35bd08be7f7f4d670e55ec362c15c0ef89625988da6be95ca9 +size 47451 diff --git a/screenshots/de/features.logout.impl_LogoutView_Day_8_de.png b/screenshots/de/features.logout.impl_LogoutView_Day_8_de.png index 71073f21cd..dd9ecc5fed 100644 --- a/screenshots/de/features.logout.impl_LogoutView_Day_8_de.png +++ b/screenshots/de/features.logout.impl_LogoutView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed9b2497d9c154f8bad06b5f0405d193f21549d9a0b4a661becce745d7203002 -size 50459 +oid sha256:652613c8509079b395d964c0290272a09b6a9bf4ca02c18d6edcae0b43e06d16 +size 61053 diff --git a/screenshots/de/features.logout.impl_LogoutView_Day_9_de.png b/screenshots/de/features.logout.impl_LogoutView_Day_9_de.png index dccb62e40c..dd9ecc5fed 100644 --- a/screenshots/de/features.logout.impl_LogoutView_Day_9_de.png +++ b/screenshots/de/features.logout.impl_LogoutView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50089edf349ad41016816bf861fb35237c2e4e4ba588846607ecdb100fa624dd -size 41703 +oid sha256:652613c8509079b395d964c0290272a09b6a9bf4ca02c18d6edcae0b43e06d16 +size 61053 diff --git a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_de.png b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_de.png index af84cc0319..1c168e3cfe 100644 --- a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:579709deae308329674cf4d47805485699f42c67e6b6779b78e95f45437aab4d -size 26045 +oid sha256:e78774044b40b63565ef540b6c1fae9fd97c105ffcc049e524e8a80d88435b50 +size 27225 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 6224a55c6d..8478c19fda 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:71815785672b31d061a25b3980ecb368c54d1e7259533fd1989819ae3c922e2a -size 74688 +oid sha256:e697b923e1e387be4bd8d3a8de0482c9be4a670192f29bb3c5529ff211e9e442 +size 71612 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 6224a55c6d..8478c19fda 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:71815785672b31d061a25b3980ecb368c54d1e7259533fd1989819ae3c922e2a -size 74688 +oid sha256:e697b923e1e387be4bd8d3a8de0482c9be4a670192f29bb3c5529ff211e9e442 +size 71612 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 4147e93eef..9462258aaf 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:a47734fb0be5195c915373f922ed2fb87f3d89c5c517fefd0fea00cad76bbca4 -size 75418 +oid sha256:4fb4462ae1261dc77869d984152bac7bd810c92a82782c36e2bcc448d3aa6459 +size 72336 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 f8fa0ef3e5..73080a8c95 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:b4c12cdd10cc2fb443c4dc248ab03740fd68cb8a2f8021225bee5010dd5e8438 -size 45465 +oid sha256:eb86dfe02f725547da2b90a4f9390db134f1f27e8c0253a33ae47ed9feb36cee +size 45289 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 44eb24b2f7..186d5c4500 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:3aa8259805201e40926ad24b4ac815c38ad03c06df4a931342d954704899366e -size 43769 +oid sha256:85c622ee5e7cc5377b07b9df0d6ed856551200af720101f084c56260d5024516 +size 43767 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 69691a898d..5db03f0da8 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:2ba5eceb0adc4b9feb3834f967dac81f1e2b6766934f6aadd54f3be011a98f6b -size 43156 +oid sha256:3644179c35846aec7a93ee45a6e0ee14ec1ca7795ca270a26cddc7c9813cedd3 +size 40448 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 69691a898d..5db03f0da8 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:2ba5eceb0adc4b9feb3834f967dac81f1e2b6766934f6aadd54f3be011a98f6b -size 43156 +oid sha256:3644179c35846aec7a93ee45a6e0ee14ec1ca7795ca270a26cddc7c9813cedd3 +size 40448 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 2add9f0e02..122e462156 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:eb7e2b92bd620115488d20e501f4040a042907dcf93d24fc77ce5a9fc178b83d -size 70404 +oid sha256:4978ac3b31f5f03fc81846e64c183a857d1f468bc1dc2816b123b92e5b2a1824 +size 67792 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 index 53994b7029..6d0b15b0f1 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1293917283d4db5644f4cdbb00a76b0071dbbde7574b8ec3376f19a0fcf6dbb9 -size 75856 +oid sha256:552461f34b6873315ec01cc013fbc3ef4a0796c9bfa0bfd445ef94e7db2d3317 +size 73308 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 index 7a1dbd9583..6e24f8a5ad 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:587ae11b72d16310df68b70367a7c1dba8611dd252be29c9e403d7dfb216f43b -size 56498 +oid sha256:e2f75acc56ee1d7e6d0381db66cc8771f787e37c51171d1d0e478045759a8997 +size 54450 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 index 2f90072e8c..822271d7a7 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54130f6586c04b3eca992f3b3ab08772c3a8dc838800dc0847aaa30fad664021 -size 68285 +oid sha256:f1c9d7db00eb7b3eb03328c7e885e8ed608e8747851e3beab4c9f2d9613fb1b3 +size 65694 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 index 81fccd7025..f0e0b4d14e 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be67cbcb3279bb0d0849ee037d4f5310d7c8e56e87906838c0c0588da19ff6ce -size 52661 +oid sha256:23ded671d28f0bf1f6f857575603e4f4a63e666ce184aa1cb24d09e868f98bef +size 51149 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 cdc5c1bd63..75a0e8a25a 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:7e065760677d14694ab6444762666f2d3af5fe8069cdbb08fdd9ddd3b5d53207 -size 44405 +oid sha256:9acf83c1389f316025d5aa95fe3ba43dc871df0cf01cdf364a228090c2873ff0 +size 41709 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 0c892f4de1..08ee8d01ed 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:c7e3df55da07e3d081934df198f12cefed4599acc8eb8b3db25907cc5062e430 -size 45029 +oid sha256:17cc8bd2c275abd9f02a6ec753c05e8af0426c3831ed51d1caa3e3b034ec48eb +size 42318 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 69691a898d..5db03f0da8 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:2ba5eceb0adc4b9feb3834f967dac81f1e2b6766934f6aadd54f3be011a98f6b -size 43156 +oid sha256:3644179c35846aec7a93ee45a6e0ee14ec1ca7795ca270a26cddc7c9813cedd3 +size 40448 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 079249afcb..efef76cdd6 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:ba49338dbd77ebbfc82d555433e03c9ed222355ac8d716dac5b1677811e55e6a -size 41844 +oid sha256:5e39e5084b58380e734fa88b624e4715489bf01411997a35f4eebcfffa11edb1 +size 39552 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 e20d4f0a54..391331f263 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:7b39a9cbacf1e86133a1031a105dec92a910a364f4758de3d48a8c63db232430 -size 44028 +oid sha256:a4b9d3c8cdbd10537eba77829cd3c91f18e6637ae1d02fcbc242cc26e860e226 +size 41319 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 fd27a4660d..700a209aba 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:e0ff3fab68e7412f4007b2fbed67268f114064097e3256d051b235cee3350296 -size 50172 +oid sha256:4c89bb4bfe847f0a71ec7b74627b21f9e20245472d16ba413416eb3705d5859d +size 50721 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 13bedfc6b3..742c8b1be9 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:deaffb7054cdb7dff2437f94f19bbb7a135e468d205fd87df9fd24a863483008 -size 46802 +oid sha256:785114ab0576fe9a69fcffa7dff1252ea660345a5706949e461878859dde0c33 +size 47362 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_de.png index 1292c01a4f..894b66623c 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90ec71c1f37b2cadc100c3ad14fc03aa1abb76881834a97c9d7db9deef49456f -size 40043 +oid sha256:ada44308165eefb690684726100507bc2c20e1d363a3555f7d8975c6645e0536 +size 40144 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 d6f33a24d0..62f0a77598 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:b9212912117ff8653d8cc2c15eb360e2da0ef68e5ef79846f3e2f00f1fab9c8a -size 62526 +oid sha256:1ae69f5a8fc884a2ca02d8c84e9342b9633512de78348a0bb2d661d5735e708a +size 63185 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 4e2a7b2671..3fc33a270d 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:d61ffb76ac311f35f3b3b5e83810e62524efbca9721f35812d1ae2386482b1ee -size 59008 +oid sha256:6930558f6c4cda30d6a83fa320a7a17d4c45db0bfb2e9bf21b13701d047e76a2 +size 59611 diff --git a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_de.png b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_de.png index b723e43390..388cb2f945 100644 --- a/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_de.png +++ b/screenshots/de/features.securebackup.impl.setup_SecureBackupSetupView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa77b425148e59b5d230f835baacc1ca3ac184a09e538057bf5d6438f20f83a4 -size 41503 +oid sha256:57dc90ddefeb2743ef616ddd11e5a9cb3847d714e2f452e2e58f7d7cc02c3422 +size 41780 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png index fc2d5b8229..4cfccb8015 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6d6cb69eb5d310ca7dae9127015fb3443325b16322c94958c4bf947fcc70514 -size 29438 +oid sha256:00d1fe85f79d7d25867287c50e50f2d0b37b52fe93c4cee70b17da485c912639 +size 30380 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png index ce7583ad06..ce9e73fd68 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84fb184c49d26ad7636588885daf3c9169209975a5b0e2f1c9ffcea6a3875683 -size 43302 +oid sha256:70c06787bdb7614fb66fb9c16a9703fc572500cbd4b7ba48d806b6c6bb68b649 +size 44169 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png index 95995db201..5c5957c19b 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2087ce92d6d532a71a1dcc3f0824a34107316030e25ce58d46696675ddd6f264 -size 42907 +oid sha256:9bcad1fdaeb5596e17cddad4498d58208eccb7dcf38efdc7434bf82badffb5ea +size 43767 diff --git a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_de.png b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_de.png index 275f38c3f5..5673458928 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2e955575444e16c332ab1e22cc121566a4f8744b8e0a01c69c5f3a9ce8ba033 -size 29507 +oid sha256:3ffba2cf2bdbed63ffd3f378a9e5b2a8c08bc316c500d7c782dbca8152c07224 +size 30449 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 0bf3fbcf3b..2cd7772aff 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,99 +1,99 @@ // 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",20567,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20573,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20567,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20567,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20567,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20567,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20567,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20567,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20567,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20567,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20567,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20567,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20567,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20573,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20573,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20573,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20573,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20573,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20573,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20573,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20573,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20573,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20573,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20573,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20567,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20567,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20573,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20573,], ["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",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20567,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20573,], ["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",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20567,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20567,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20567,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20567,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20567,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20567,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20570,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20567,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20567,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20567,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20567,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20573,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20573,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20573,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20573,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20573,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20573,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20573,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20573,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20573,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20567,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20567,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20567,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20567,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20567,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20573,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20573,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20573,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20573,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20573,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20570,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20570,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20570,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20567,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20573,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20573,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20573,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20573,], ["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",20567,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20573,], ["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",20567,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20573,], ["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",20567,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20573,], ["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",20567,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20573,], ["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,], @@ -103,19 +103,19 @@ 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_AttachmentsPreviewView_0_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20567,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20567,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20573,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20567,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20573,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -145,22 +145,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20567,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20573,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20567,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20567,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20573,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20567,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20567,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20567,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20567,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20567,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20573,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20573,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20573,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20573,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20573,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -171,140 +171,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20567,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20567,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20573,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20573,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20567,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20573,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20567,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20567,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20567,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20567,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20567,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20567,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20573,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20573,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20573,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20573,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20573,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20573,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20567,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20567,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20567,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20573,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20573,], ["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",20567,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20567,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20567,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20567,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20567,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20573,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20573,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20573,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20573,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20573,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20567,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20573,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20567,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20567,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20567,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20567,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20567,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20567,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20567,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20567,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20573,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20573,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20573,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20573,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20573,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20573,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20573,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20573,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20577,], ["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",20567,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20567,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20567,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20567,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20567,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20567,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20567,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20573,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20573,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20567,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20567,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20567,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20567,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20567,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20567,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20573,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20573,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20573,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20573,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20573,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20567,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20567,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20567,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20567,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20567,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20567,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20567,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20567,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20567,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20567,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20567,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20567,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20567,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20567,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20567,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20567,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20567,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20567,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20567,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20567,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20573,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20573,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20573,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20573,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20573,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20573,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20573,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20573,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20573,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20573,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20573,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20573,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20573,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20573,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20573,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20573,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20573,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20573,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20573,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20573,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20567,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20567,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20567,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20567,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20567,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20567,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20567,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20573,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20573,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20573,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20573,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20573,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20573,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20573,], ["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",20567,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20567,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20567,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20573,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20573,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20573,], ["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",20567,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20573,], ["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",20567,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20567,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20567,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20567,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20567,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20567,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20567,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20567,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20567,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20573,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20573,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20573,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20573,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20573,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20573,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20573,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20573,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20573,], ["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,], @@ -319,20 +320,20 @@ 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",20567,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20567,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20567,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20567,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20567,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20567,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20567,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20567,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20567,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20567,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20567,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20567,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20567,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20570,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20573,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20573,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20573,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20573,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20573,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20573,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20573,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20573,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20573,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20573,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20573,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20573,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20573,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20573,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -340,28 +341,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20567,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20567,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20573,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20573,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20567,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20567,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20567,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20567,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20567,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20567,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20567,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20567,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20567,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20567,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20573,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20573,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20573,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20573,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20573,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20573,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20573,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20573,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20573,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20577,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -381,49 +383,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20567,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20567,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20567,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20573,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20573,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20573,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20567,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20567,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20573,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20573,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20570,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20573,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20567,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20567,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20567,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20567,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20567,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20573,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20573,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20573,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20573,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20573,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20567,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20567,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20573,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20573,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20567,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20567,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20573,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20573,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20567,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20567,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20567,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20567,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20567,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20567,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20567,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20567,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20567,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20567,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20567,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20567,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20567,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20573,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20573,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20573,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20573,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20573,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20573,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20573,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20573,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20573,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20573,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20573,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20573,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20573,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -438,8 +440,8 @@ export const screenshots = [ ["appicon.element_Icon_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",20567,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20567,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20573,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20573,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -447,119 +449,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20567,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20573,], ["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",20567,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20573,], ["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",20567,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20567,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20573,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20573,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20567,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20567,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20573,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20567,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20567,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20570,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20573,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20567,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20573,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20567,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20567,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20567,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20567,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20573,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20567,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20567,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20567,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20573,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20573,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20573,], ["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",20567,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20567,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20567,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20567,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20567,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20573,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20573,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20573,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20567,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20567,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20573,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20573,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20567,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20567,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20567,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20573,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20573,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20567,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20567,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20567,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20567,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20567,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20567,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20573,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20567,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20573,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -614,45 +616,45 @@ 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",20567,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20573,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20567,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20567,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20567,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20573,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20573,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20573,], ["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",20567,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20567,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20567,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20567,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20567,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20567,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20567,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20567,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20567,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20567,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20567,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20567,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20567,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20570,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20570,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20567,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20567,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20567,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20567,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20567,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20567,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20567,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20567,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20567,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20567,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20567,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20567,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20573,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20573,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20573,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20573,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20573,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20573,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20573,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20573,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20573,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20573,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20573,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20573,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20573,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20573,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20573,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20573,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20573,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20573,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20573,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20573,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20573,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20573,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20573,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20573,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20573,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20573,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20573,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20567,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20567,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20567,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20567,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20573,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20573,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20573,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20573,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -665,26 +667,26 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20567,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20570,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20567,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20570,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20570,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20570,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20573,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20573,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20573,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20573,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20573,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20573,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20567,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20567,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20573,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -692,15 +694,15 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20570,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20570,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20570,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20570,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], @@ -710,15 +712,15 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20567,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20567,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20567,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20567,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20573,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -732,7 +734,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",20567,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20573,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -741,7 +743,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20567,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20573,], ["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,], @@ -750,23 +752,23 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20567,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20567,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20567,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20567,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20567,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20567,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20567,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20567,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20567,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20567,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20567,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20567,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20567,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20567,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20567,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20573,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20573,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20573,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20573,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20573,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20573,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20573,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20573,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20573,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20573,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20573,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20573,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20573,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20573,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20573,], ["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",20567,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20573,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -777,113 +779,113 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20567,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20567,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20567,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20573,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20573,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20573,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20567,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20570,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20573,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20567,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20573,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20567,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20567,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20573,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20567,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20567,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20567,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20567,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20567,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20567,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20573,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20573,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20573,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20573,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20573,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20573,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20567,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20567,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20573,], ["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",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20567,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20567,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20567,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20567,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20567,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20567,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20573,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20573,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20573,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20573,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20573,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20567,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20567,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20567,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20567,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20567,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20573,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20573,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20573,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20573,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20573,], ["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",20567,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20567,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20567,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20567,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20567,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20567,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20567,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20567,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20567,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20567,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20567,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20573,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20573,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20573,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20573,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20573,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20573,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20573,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20573,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20573,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20573,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20573,], ["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,], @@ -897,223 +899,225 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20567,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20567,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20567,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20567,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20570,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20570,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20573,], ["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","",20567,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20567,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20573,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20573,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20567,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20567,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20567,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20567,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20567,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20567,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20567,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20567,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20567,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20567,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20567,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20567,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20567,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20567,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20567,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20567,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20573,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20573,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20573,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20573,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20573,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20573,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20573,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20573,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20573,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20573,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20573,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20573,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20573,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20573,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20573,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20573,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20567,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20567,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20573,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20573,], ["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",20567,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20567,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20567,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20567,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20567,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20567,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20567,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20573,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20573,], ["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",20567,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20567,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20567,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20567,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20567,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20567,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20567,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20567,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20567,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20567,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20567,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20567,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20567,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20567,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20567,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20567,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20567,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20573,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20573,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20573,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20573,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20573,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20573,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20573,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20573,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20573,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20573,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20573,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20573,], ["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",20567,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20567,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20567,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20567,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20567,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20573,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20573,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20573,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20573,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20573,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20567,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20567,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20573,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20573,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20567,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20567,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20567,], -["features.roomdetails.impl_RoomDetails_0_en","",20567,], -["features.roomdetails.impl_RoomDetails_10_en","",20567,], -["features.roomdetails.impl_RoomDetails_11_en","",20567,], -["features.roomdetails.impl_RoomDetails_12_en","",20567,], -["features.roomdetails.impl_RoomDetails_13_en","",20567,], -["features.roomdetails.impl_RoomDetails_14_en","",20567,], -["features.roomdetails.impl_RoomDetails_15_en","",20567,], -["features.roomdetails.impl_RoomDetails_16_en","",20567,], -["features.roomdetails.impl_RoomDetails_17_en","",20567,], -["features.roomdetails.impl_RoomDetails_18_en","",20567,], -["features.roomdetails.impl_RoomDetails_19_en","",20567,], -["features.roomdetails.impl_RoomDetails_1_en","",20567,], -["features.roomdetails.impl_RoomDetails_20_en","",20567,], -["features.roomdetails.impl_RoomDetails_21_en","",20567,], -["features.roomdetails.impl_RoomDetails_22_en","",20567,], -["features.roomdetails.impl_RoomDetails_2_en","",20567,], -["features.roomdetails.impl_RoomDetails_3_en","",20567,], -["features.roomdetails.impl_RoomDetails_4_en","",20567,], -["features.roomdetails.impl_RoomDetails_5_en","",20567,], -["features.roomdetails.impl_RoomDetails_6_en","",20567,], -["features.roomdetails.impl_RoomDetails_7_en","",20567,], -["features.roomdetails.impl_RoomDetails_8_en","",20567,], -["features.roomdetails.impl_RoomDetails_9_en","",20567,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20567,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20567,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20567,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20567,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20567,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20567,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20567,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20567,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20567,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20573,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20573,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20573,], +["features.roomdetails.impl_RoomDetails_0_en","",20573,], +["features.roomdetails.impl_RoomDetails_10_en","",20573,], +["features.roomdetails.impl_RoomDetails_11_en","",20573,], +["features.roomdetails.impl_RoomDetails_12_en","",20573,], +["features.roomdetails.impl_RoomDetails_13_en","",20573,], +["features.roomdetails.impl_RoomDetails_14_en","",20573,], +["features.roomdetails.impl_RoomDetails_15_en","",20573,], +["features.roomdetails.impl_RoomDetails_16_en","",20573,], +["features.roomdetails.impl_RoomDetails_17_en","",20573,], +["features.roomdetails.impl_RoomDetails_18_en","",20573,], +["features.roomdetails.impl_RoomDetails_19_en","",20573,], +["features.roomdetails.impl_RoomDetails_1_en","",20573,], +["features.roomdetails.impl_RoomDetails_20_en","",20573,], +["features.roomdetails.impl_RoomDetails_21_en","",20573,], +["features.roomdetails.impl_RoomDetails_22_en","",20573,], +["features.roomdetails.impl_RoomDetails_2_en","",20573,], +["features.roomdetails.impl_RoomDetails_3_en","",20573,], +["features.roomdetails.impl_RoomDetails_4_en","",20573,], +["features.roomdetails.impl_RoomDetails_5_en","",20573,], +["features.roomdetails.impl_RoomDetails_6_en","",20573,], +["features.roomdetails.impl_RoomDetails_7_en","",20573,], +["features.roomdetails.impl_RoomDetails_8_en","",20573,], +["features.roomdetails.impl_RoomDetails_9_en","",20573,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20573,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20573,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20573,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20573,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20573,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20573,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20573,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20573,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20573,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20567,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20567,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20567,], -["features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Night_0_en",20567,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20567,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20567,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_0_en",20567,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_1_en",20567,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_2_en",20567,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20573,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20573,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20573,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20577,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20577,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20577,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20577,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20577,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20577,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20573,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20573,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20567,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20567,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20567,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20573,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20573,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20573,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20567,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20567,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20573,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20567,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20567,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20567,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20567,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20567,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20567,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20573,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1136,16 +1140,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20567,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20567,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20573,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20567,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20573,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1154,119 +1158,119 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20567,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20573,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20567,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20567,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20573,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20573,], ["appicon.element_RoundIcon_en","",0,], ["appicon.enterprise_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20567,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20567,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20567,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20567,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20567,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20567,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20567,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20567,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20573,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20573,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20573,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20573,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20573,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20573,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20573,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20573,], ["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","",20567,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20573,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20567,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20567,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20567,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20567,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20567,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20567,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20567,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20567,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20567,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20567,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20567,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20567,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20567,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20567,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20567,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20573,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20573,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20573,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20573,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20573,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20573,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20573,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20573,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20573,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20573,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20573,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20573,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20573,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20573,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20573,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1289,35 +1293,35 @@ 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.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20567,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20567,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20567,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20567,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20567,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20567,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20567,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20567,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20567,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20567,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20567,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20567,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20567,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20567,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20567,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20573,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20573,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20573,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20573,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20573,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20573,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20573,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20573,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20573,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20573,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20573,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20573,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20573,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20573,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20573,], ["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",20567,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20567,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20567,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20567,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20567,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20567,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20567,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20570,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20570,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20567,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20567,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20573,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20573,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20573,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20573,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20573,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20573,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20573,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20573,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20573,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20573,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20573,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1327,107 +1331,107 @@ 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",20567,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20573,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20567,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20567,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20567,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20570,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20567,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20573,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20573,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20573,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20573,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20573,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20567,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20567,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20567,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20567,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20567,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20567,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20567,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20567,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20567,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20567,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20567,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20567,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20567,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20567,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20567,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20573,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20573,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20573,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20573,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20573,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20573,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20573,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20573,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20573,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20573,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20573,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20573,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20573,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20573,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20567,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20567,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20567,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20567,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20567,], -["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20567,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20567,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20573,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20573,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20573,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20573,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20573,], +["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20573,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20573,], ["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",20567,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20573,], ["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",20567,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20573,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20567,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20567,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20567,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20567,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20567,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20567,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20567,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20567,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20567,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20567,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20567,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20567,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20570,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20567,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20567,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20567,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20573,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20573,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20573,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20573,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20573,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20573,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20573,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20573,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20573,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20573,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20573,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20573,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20573,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20573,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20573,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20573,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20567,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20567,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20573,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20573,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1440,17 +1444,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20567,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20567,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20573,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20573,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20567,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20567,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20567,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20573,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20573,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20573,], ["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",20567,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20567,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20573,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20573,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1460,18 +1464,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20567,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20573,], ["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",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20567,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20573,], ["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,], @@ -1479,18 +1483,18 @@ 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",20567,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20573,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20567,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20567,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20573,], ["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",20567,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20573,], ["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,], @@ -1499,44 +1503,44 @@ 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",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20573,], ["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",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20573,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20567,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20573,], ["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","",20567,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20573,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20567,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20567,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20573,], ["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",20567,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20573,], ["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",20570,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20570,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20570,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20570,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20567,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20567,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20567,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20573,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20573,], ["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",20567,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20567,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20573,], ["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",20567,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20573,], ["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,], @@ -1545,8 +1549,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",20567,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20567,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20573,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20573,], ["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,], @@ -1561,8 +1565,8 @@ 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",20567,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20567,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20573,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1585,84 +1589,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20567,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20570,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20567,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20573,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20573,], ["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",20567,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20573,], ["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",20567,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20573,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20567,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20567,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20573,], ["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",20567,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20567,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20567,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20567,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20567,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20567,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20573,], ["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","",20567,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20573,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20567,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20567,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20567,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20567,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20573,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20573,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20573,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20573,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20567,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20573,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20567,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20573,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20567,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20567,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20567,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20567,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20567,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20567,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20567,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20567,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20567,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20567,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20567,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20567,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20567,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20573,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20573,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20573,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20573,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20573,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20573,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20573,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20573,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20573,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20573,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20573,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20573,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20573,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20567,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20567,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20573,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20573,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20567,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20573,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png index 16668cae13..c4a683e701 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d62d11d8d8ccbbbbe811f157ecc20f31096a017f4acb36061a29e2aaefbd3e8 -size 25132 +oid sha256:44e078c64ab932a6e9ce2d429efb5f44556d4a923f4949e75dd2bd99591c4533 +size 25242 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png index 20a22487c4..2a6cbdf049 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.changeserver_ChangeServerView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6d0f6746f6ccea9db09a6d351e5aae7d10d4d623edf00059bfece647731c2e5 -size 23959 +oid sha256:a2d413d264af067d4e03743cd50be879c91796a3f0907f40cefcdc6b0e466897 +size 24020 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png index 16668cae13..c4a683e701 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d62d11d8d8ccbbbbe811f157ecc20f31096a017f4acb36061a29e2aaefbd3e8 -size 25132 +oid sha256:44e078c64ab932a6e9ce2d429efb5f44556d4a923f4949e75dd2bd99591c4533 +size 25242 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png index 20a22487c4..2a6cbdf049 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.login_LoginModeView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6d0f6746f6ccea9db09a6d351e5aae7d10d4d623edf00059bfece647731c2e5 -size 23959 +oid sha256:a2d413d264af067d4e03743cd50be879c91796a3f0907f40cefcdc6b0e466897 +size 24020 From e81ab35f5b9f483cf412d50dfe545923292579c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 08:06:30 +0200 Subject: [PATCH 072/471] Update dependency io.nlopez.compose.rules:detekt to v0.5.8 (#6711) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index dfd32f0872..474b868eda 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,7 +46,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.5.7") + detektPlugins("io.nlopez.compose.rules:detekt:0.5.8") detektPlugins(project(":tests:detekt-rules")) } From 5733c3149a7d7cfa319a7ca8e8b9cac7657a7a7e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 08:07:11 +0200 Subject: [PATCH 073/471] Update dependency com.posthog:posthog-android to v3.43.0 (#6704) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a06ccd49c..3c73a55a15 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -221,7 +221,7 @@ haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics -posthog = "com.posthog:posthog-android:3.39.0" +posthog = "com.posthog:posthog-android:3.43.0" sentry = "io.sentry:sentry-android:8.40.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.33.2" From 27869c0e0427b8a9c289399930cfac0743efcfcf Mon Sep 17 00:00:00 2001 From: Hi Dude! Date: Mon, 4 May 2026 09:30:05 +0300 Subject: [PATCH 074/471] Fix calls on Huawei devices: skip addWebMessageListener on Chromium < 119 (#6640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix calls on Huawei: skip addWebMessageListener on Chromium < 119 * Fix lint issues, log webview version --------- Co-authored-by: manfrommedan Co-authored-by: Jorge Martín --- .../utils/WebViewWidgetMessageInterceptor.kt | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt index f7ab2c57af..c74ae90abd 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewWidgetMessageInterceptor.kt @@ -140,26 +140,33 @@ class WebViewWidgetMessageInterceptor( } } - // Create a WebMessageListener, which will receive messages from the WebView and reply to them - val webMessageListener = WebViewCompat.WebMessageListener { _, message, _, _, _ -> - onMessageReceived(message.data) - } + // Always register JavascriptInterface as the baseline message channel. + // This works on all WebView implementations including Huawei. + webView.addJavascriptInterface(object { + @JavascriptInterface + fun postMessage(json: String?) { + onMessageReceived(json) + } + }, LISTENER_NAME) - // Use WebMessageListener if supported, otherwise use JavascriptInterface - if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { + // Additionally register WebMessageListener on WebViews that reliably support it. + // Huawei WebView (Chromium < 119) reports WEB_MESSAGE_LISTENER as supported + // but silently drops messages, so we only trust it on Chromium 119+. + // See: https://github.com/element-hq/element-x-android/issues/6632 + val webViewVersionName = WebViewCompat.getCurrentWebViewPackage(webView.context)?.versionName.orEmpty() + Timber.d("Using WebView version: $webViewVersionName") + val webViewVersionCode = webViewVersionName.split(".").firstOrNull()?.toIntOrNull() ?: 0 + + if (webViewVersionCode >= 119 && + WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) { WebViewCompat.addWebMessageListener( webView, LISTENER_NAME, setOf("*"), - webMessageListener - ) - } else { - webView.addJavascriptInterface(object { - @JavascriptInterface - fun postMessage(json: String?) { - onMessageReceived(json) + WebViewCompat.WebMessageListener { _, message, _, _, _ -> + onMessageReceived(message.data) } - }, LISTENER_NAME) + ) } } From a0646717a313072ba287c70a158e84f99eaaf832 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 4 May 2026 08:59:46 +0200 Subject: [PATCH 075/471] fix test compilation --- .../eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index e1e8717c4c..177cc27df2 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -75,6 +75,7 @@ class DefaultRoomLatestEventFormatterTest { profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), stateContentFormatter = StateContentFormatter(stringProvider), permalinkParser = FakePermalinkParser(), + rtcNotificationContentFormatter = RtcNotificationContentFormatter(fakeMatrixClient, stringProvider) ) } From 61f074ddc1764aaf782842d16f55ecf9e96b9377 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 30 Apr 2026 14:18:15 +0200 Subject: [PATCH 076/471] Let our Json parser accept comments and trailing comma. --- .../android/libraries/androidutils/json/JsonProvider.kt | 9 ++++++++- .../impl/DefaultSessionWellknownRetrieverTest.kt | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt index 1e25599962..12f2957678 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/json/JsonProvider.kt @@ -23,6 +23,13 @@ fun interface JsonProvider { @ContributesBinding(AppScope::class) @SingleIn(AppScope::class) class DefaultJsonProvider : JsonProvider { - private val json: Json by lazy { Json { ignoreUnknownKeys = true } } + private val json: Json by lazy { + Json { + ignoreUnknownKeys = true + allowComments = true + allowTrailingComma = true + } + } + override fun invoke() = json } diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index faad139a36..e8c97c0220 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -80,7 +80,8 @@ class DefaultSessionWellknownRetrieverTest { "registration_helper_url": "a_registration_url", "enforce_element_pro": true, "rageshake_url": "a_rageshake_url", - "other": true + // Note the trailing comma, and the comment! + "other": true, }""".trimIndent().toByteArray() ) }, From 54525d855e7fdb7104f28f6fa5016312d5a2e160 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 10:04:20 +0000 Subject: [PATCH 077/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4 (#6718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4 * Fix `RoomInfo` fixture --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- gradle/libs.versions.toml | 2 +- .../libraries/matrix/impl/fixtures/factories/RoomInfo.kt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c73a55a15..8d91c8a7be 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.04.27" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.4" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt index 491614a7fc..2ce64154f7 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt @@ -63,6 +63,7 @@ internal fun aRustRoomInfo( isLowPriority: Boolean = false, activeRoomCallConsensusIntent: RtcCallIntentConsensus = RtcCallIntentConsensus.None, activeServiceMembersCount: Int = 0, + isDm: Boolean = false, ) = RoomInfo( id = id, displayName = displayName, @@ -103,4 +104,5 @@ internal fun aRustRoomInfo( isLowPriority = isLowPriority, activeRoomCallConsensusIntent = activeRoomCallConsensusIntent, activeServiceMembersCount = activeServiceMembersCount.toULong(), + isDm = isDm, ) From 6897cc57212a1746c3efa334380cacfbdd013a82 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:07:15 +0000 Subject: [PATCH 078/471] Add clipping to RoomSummaryRow --- .../home/impl/components/RoomSummaryRow.kt | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index f541417104..72ecb5046d 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString @@ -222,20 +223,17 @@ private fun NameAndTimestampRow( modifier = modifier.fillMaxWidth(), horizontalArrangement = spacedBy(16.dp) ) { - Row( - modifier = Modifier.weight(1f), - verticalAlignment = Alignment.CenterVertically, - ) { - // Name - Text( - style = ElementTheme.typography.fontBodyLgMedium, - text = name?.toSafeLength(ellipsize = true) ?: stringResource(id = CommonStrings.common_no_room_name), - fontStyle = FontStyle.Italic.takeIf { name == null }, - color = ElementTheme.colors.roomListRoomName, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } + Text( + modifier = Modifier + .weight(1f) + .clipToBounds(), + style = ElementTheme.typography.fontBodyLgMedium, + text = name?.toSafeLength(ellipsize = true) ?: stringResource(id = CommonStrings.common_no_room_name), + fontStyle = FontStyle.Italic.takeIf { name == null }, + color = ElementTheme.colors.roomListRoomName, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) // Timestamp Text( text = timestamp ?: "", @@ -262,12 +260,12 @@ private fun InviteSubtitle( } if (subtitle != null) { Text( + modifier = modifier.clipToBounds(), text = subtitle, maxLines = 1, overflow = TextOverflow.Ellipsis, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.roomListRoomMessage, - modifier = modifier, ) } } @@ -326,7 +324,9 @@ private fun MessagePreviewAndIndicatorRow( val messagePreview = room.latestEvent.content() val annotatedMessagePreview = messagePreview as? AnnotatedString ?: AnnotatedString(text = messagePreview.orEmpty().toString()) Text( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .clipToBounds(), text = annotatedMessagePreview, color = ElementTheme.colors.roomListRoomMessage, style = ElementTheme.typography.fontBodyMdRegular, @@ -381,7 +381,9 @@ private fun InviteNameAndIndicatorRow( verticalAlignment = Alignment.CenterVertically, ) { Text( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .clipToBounds(), style = ElementTheme.typography.fontBodyLgMedium, text = name?.toSafeLength(ellipsize = true) ?: stringResource(id = CommonStrings.common_no_room_name), fontStyle = FontStyle.Italic.takeIf { name == null }, From ab85c554ff47707d0e864fafd00d92db6254d5fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 00:49:45 +0000 Subject: [PATCH 079/471] Update metro to v1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d91c8a7be..ad7592f0a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,7 +54,7 @@ haze = "1.7.2" dependencyAnalysis = "3.9.0" # DI -metro = "0.13.2" +metro = "1.0.0" # Auto service autoservice = "1.1.1" From c40f916b4fa3d8ea9fc61c1be998a57635ea40fb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 10:55:16 +0200 Subject: [PATCH 080/471] Merge pull request #6722 from element-hq/renovate/roborazzi Update roborazzi to v1.60.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d91c8a7be..d17ddc99d7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.59.0" +roborazzi = "1.60.0" # Jetbrain datetime = "0.7.1" From 51bcaca9db698ee007efe42cfba61ee39fa4cad3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 May 2026 12:17:51 +0200 Subject: [PATCH 081/471] Rename methods around verification, to match SDK naming. --- .../OutgoingVerificationStateMachine.kt | 4 +- .../OutgoingVerificationPresenterTest.kt | 52 +++++++++---------- .../SessionVerificationService.kt | 6 +-- .../RustSessionVerificationService.kt | 4 +- .../FakeSessionVerificationService.kt | 12 ++--- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt index 7932ccc484..465fc20066 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationStateMachine.kt @@ -46,7 +46,7 @@ class OutgoingVerificationStateMachine( inState { onEnterEffect { event -> when (event.verificationRequest) { - is VerificationRequest.Outgoing.CurrentSession -> sessionVerificationService.requestCurrentSessionVerification() + is VerificationRequest.Outgoing.CurrentSession -> sessionVerificationService.requestDeviceVerification() is VerificationRequest.Outgoing.User -> sessionVerificationService.requestUserVerification(event.verificationRequest.userId) } } @@ -56,7 +56,7 @@ class OutgoingVerificationStateMachine( } inState { onEnterEffect { - sessionVerificationService.startVerification() + sessionVerificationService.startSasVerification() } } inState { diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index f02f24c9ef..da31b633b1 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -50,11 +50,11 @@ class OutgoingVerificationPresenterTest { @Test fun `present - Handles requestVerification for session verification`() = runTest { - val requestSessionVerificationRecorder = lambdaRecorder {} - val startVerificationRecorder = lambdaRecorder {} + val requestDeviceVerificationRecorder = lambdaRecorder {} + val startSasVerificationRecorder = lambdaRecorder {} val service = unverifiedSessionService( - requestSessionVerificationLambda = requestSessionVerificationRecorder, - startVerificationLambda = startVerificationRecorder, + requestDeviceVerificationLambda = requestDeviceVerificationRecorder, + startSasVerificationLambda = startSasVerificationRecorder, ) val presenter = createOutgoingVerificationPresenter( service = service, @@ -63,18 +63,18 @@ class OutgoingVerificationPresenterTest { presenter.test { requestVerificationAndAwaitVerifyingState(service) - requestSessionVerificationRecorder.assertions().isCalledOnce() - startVerificationRecorder.assertions().isCalledOnce() + requestDeviceVerificationRecorder.assertions().isCalledOnce() + startSasVerificationRecorder.assertions().isCalledOnce() } } @Test fun `present - Handles requestVerification for user verification`() = runTest { val requestUserVerificationRecorder = lambdaRecorder {} - val startVerificationRecorder = lambdaRecorder {} + val startSasVerificationRecorder = lambdaRecorder {} val service = unverifiedSessionService( requestUserVerificationLambda = requestUserVerificationRecorder, - startVerificationLambda = startVerificationRecorder, + startSasVerificationLambda = startSasVerificationRecorder, ) val presenter = createOutgoingVerificationPresenter( service = service, @@ -84,7 +84,7 @@ class OutgoingVerificationPresenterTest { requestVerificationAndAwaitVerifyingState(service) requestUserVerificationRecorder.assertions().isCalledOnce() - startVerificationRecorder.assertions().isCalledOnce() + startSasVerificationRecorder.assertions().isCalledOnce() } } @@ -106,8 +106,8 @@ class OutgoingVerificationPresenterTest { @Test fun `present - A failure when verifying cancels it`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, approveVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) @@ -125,7 +125,7 @@ class OutgoingVerificationPresenterTest { @Test fun `present - A fail when requesting verification resets the state to the canceled one`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, + requestDeviceVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) presenter.test { @@ -139,8 +139,8 @@ class OutgoingVerificationPresenterTest { @Test fun `present - Canceling the flow once it's verifying cancels it`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, cancelVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) @@ -154,8 +154,8 @@ class OutgoingVerificationPresenterTest { @Test fun `present - When verifying, if we receive another challenge we ignore it`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) presenter.test { @@ -168,8 +168,8 @@ class OutgoingVerificationPresenterTest { @Test fun `present - Go back after cancellation returns to initial state`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) presenter.test { @@ -189,8 +189,8 @@ class OutgoingVerificationPresenterTest { VerificationEmoji(number = 30) ) val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, approveVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) @@ -215,8 +215,8 @@ class OutgoingVerificationPresenterTest { @Test fun `present - When verification is declined, the flow is canceled`() = runTest { val service = unverifiedSessionService( - requestSessionVerificationLambda = { }, - startVerificationLambda = { }, + requestDeviceVerificationLambda = { }, + startSasVerificationLambda = { }, declineVerificationLambda = { }, ) val presenter = createOutgoingVerificationPresenter(service) @@ -298,23 +298,23 @@ class OutgoingVerificationPresenterTest { } private suspend fun unverifiedSessionService( - requestSessionVerificationLambda: () -> Unit = { lambdaError() }, + requestDeviceVerificationLambda: () -> Unit = { lambdaError() }, requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() }, cancelVerificationLambda: () -> Unit = { lambdaError() }, approveVerificationLambda: () -> Unit = { lambdaError() }, declineVerificationLambda: () -> Unit = { lambdaError() }, - startVerificationLambda: () -> Unit = { lambdaError() }, + startSasVerificationLambda: () -> Unit = { lambdaError() }, resetLambda: (Boolean) -> Unit = { }, acknowledgeVerificationRequestLambda: (VerificationRequest.Incoming) -> Unit = { lambdaError() }, acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, ): FakeSessionVerificationService { return FakeSessionVerificationService( - requestCurrentSessionVerificationLambda = requestSessionVerificationLambda, + requestDeviceVerificationLambda = requestDeviceVerificationLambda, requestUserVerificationLambda = requestUserVerificationLambda, cancelVerificationLambda = cancelVerificationLambda, approveVerificationLambda = approveVerificationLambda, declineVerificationLambda = declineVerificationLambda, - startVerificationLambda = startVerificationLambda, + startSasVerificationLambda = startSasVerificationLambda, resetLambda = resetLambda, acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, acceptVerificationRequestLambda = acceptVerificationRequestLambda, 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 4d2e786038..38a00d833d 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 @@ -33,7 +33,7 @@ interface SessionVerificationService { /** * Request verification of the current session. */ - suspend fun requestCurrentSessionVerification() + suspend fun requestDeviceVerification() /** * Request verification of the user with the given [userId]. @@ -56,9 +56,9 @@ interface SessionVerificationService { suspend fun declineVerification() /** - * Starts the verification of the unverified session from another device. + * Transition the current verification request into a SAS verification flow. */ - suspend fun startVerification() + suspend fun startSasVerification() /** * Returns the verification service state to the initial step. 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 7fb2935897..b2a13f5f1a 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 @@ -124,7 +124,7 @@ class RustSessionVerificationService( this.listener = listener } - override suspend fun requestCurrentSessionVerification() = tryOrFail { + override suspend fun requestDeviceVerification() = tryOrFail { ensureEncryptionIsInitialized() verificationController.requestDeviceVerification() currentVerificationRequest = VerificationRequest.Outgoing.CurrentSession @@ -146,7 +146,7 @@ class RustSessionVerificationService( override suspend fun declineVerification() = tryOrFail { verificationController.declineVerification() } - override suspend fun startVerification() = tryOrFail { + override suspend fun startSasVerification() = tryOrFail { verificationController.startSasVerification() } 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 f4f12a435e..dab4d48952 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 @@ -22,12 +22,12 @@ import kotlinx.coroutines.flow.StateFlow class FakeSessionVerificationService( initialSessionVerifiedStatus: SessionVerifiedStatus = SessionVerifiedStatus.Unknown, - private val requestCurrentSessionVerificationLambda: () -> Unit = { lambdaError() }, + private val requestDeviceVerificationLambda: () -> Unit = { lambdaError() }, private val requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() }, private val cancelVerificationLambda: () -> Unit = { lambdaError() }, private val approveVerificationLambda: () -> Unit = { lambdaError() }, private val declineVerificationLambda: () -> Unit = { lambdaError() }, - private val startVerificationLambda: () -> Unit = { lambdaError() }, + private val startSasVerificationLambda: () -> Unit = { lambdaError() }, private val resetLambda: (Boolean) -> Unit = { lambdaError() }, private val acknowledgeVerificationRequestLambda: (VerificationRequest.Incoming) -> Unit = { lambdaError() }, private val acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, @@ -40,8 +40,8 @@ class FakeSessionVerificationService( override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus override val needsSessionVerification: Flow = _needsSessionVerification - override suspend fun requestCurrentSessionVerification() { - requestCurrentSessionVerificationLambda() + override suspend fun requestDeviceVerification() { + requestDeviceVerificationLambda() } override suspend fun requestUserVerification(userId: UserId) { @@ -60,8 +60,8 @@ class FakeSessionVerificationService( declineVerificationLambda() } - override suspend fun startVerification() { - startVerificationLambda() + override suspend fun startSasVerification() { + startSasVerificationLambda() } override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) { From a12b5191558ec080f18fb6e95c35e1204ac4a207 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 5 May 2026 13:14:30 +0200 Subject: [PATCH 082/471] Allow cancelling room loading in Home screen (#6723) Previously, this was disabled by mistake, since it's the default behavior. --- .../io/element/android/features/home/impl/HomeFlowNode.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index 81e7969080..00f285e3f4 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope import com.bumble.appyx.core.lifecycle.subscribe @@ -171,6 +172,7 @@ class HomeFlowNode( if (loadingJoinedRoomJob.value.isLoading()) { DelayedVisibility(duration = 400.milliseconds) { ProgressDialog( + properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true), onDismissRequest = { loadingJoinedRoomJob.value.dataOrNull()?.cancel() loadingJoinedRoomJob.value = AsyncData.Uninitialized From 2d203e83b9dcbab198e43b62d5f1c8000d64c1aa Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 5 May 2026 15:24:27 +0200 Subject: [PATCH 083/471] Revert PR #6642 (#6724) * Revert "Change native back button behavior in EC view (close settings in EC with os native back) (#6642)" This reverts commit 6ba4679908ae1a3c7b0f698c10fda649aff9f8a5. * Fix API breaks from revert --- .../call/impl/ui/CallScreenBackPressPolicy.kt | 26 --- .../features/call/impl/ui/CallScreenView.kt | 36 ++--- .../call/ui/CallScreenBackPressPolicyTest.kt | 96 ------------ .../features/call/ui/CallScreenViewTest.kt | 148 ------------------ 4 files changed, 10 insertions(+), 296 deletions(-) delete mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt delete mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt delete mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt deleted file mode 100644 index cd47cd8bb1..0000000000 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.impl.ui -internal sealed interface CallScreenBackPressAction { - data object DispatchEscapeToWebView : CallScreenBackPressAction - data object EnterPictureInPicture : CallScreenBackPressAction -} - -internal object CallScreenBackPressPolicy { - fun resolve( - supportPip: Boolean, - hasWebView: Boolean, - fromNative: Boolean, - ): CallScreenBackPressAction? { - return when { - hasWebView && fromNative -> CallScreenBackPressAction.DispatchEscapeToWebView - hasWebView && supportPip -> CallScreenBackPressAction.EnterPictureInPicture - else -> null - } - } -} 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 ea3668316b..1c68a62f55 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 @@ -64,15 +64,11 @@ internal fun CallScreenView( requestPermissions: (Array, RequestPermissionCallback) -> Unit, modifier: Modifier = Modifier, ) { - var callWebView by remember { mutableStateOf(null) } - - fun handleBack(fromNative: Boolean = false) { - when (CallScreenBackPressPolicy.resolve(supportPip = pipState.supportPip, hasWebView = callWebView != null, fromNative)) { - CallScreenBackPressAction.EnterPictureInPicture -> - pipState.eventSink(PictureInPictureEvent.EnterPictureInPicture) - CallScreenBackPressAction.DispatchEscapeToWebView -> - callWebView?.dispatchEscKeyEvent() - null -> Timber.d("Back press with unsupported pip is a no-op") + fun handleBack() { + if (pipState.supportPip) { + pipState.eventSink.invoke(PictureInPictureEvent.EnterPictureInPicture) + } else { + state.eventSink(CallScreenEvent.Hangup) } } @@ -80,7 +76,7 @@ internal fun CallScreenView( modifier = modifier, ) { padding -> BackHandler { - handleBack(fromNative = true) + handleBack() } if (state.webViewError != null) { ErrorDialog( @@ -115,7 +111,6 @@ internal fun CallScreenView( }, onConsoleMessage = onConsoleMessage, onCreateWebView = { webView -> - callWebView = webView webView.addBackHandler(onBackPressed = ::handleBack) val interceptor = WebViewWidgetMessageInterceptor( webView = webView, @@ -140,7 +135,6 @@ internal fun CallScreenView( pipState.eventSink(PictureInPictureEvent.SetPipController(pipController)) }, onDestroyWebView = { - callWebView = null // Reset audio mode webViewAudioManager?.onCallStopped() } @@ -149,7 +143,6 @@ internal fun CallScreenView( AsyncData.Uninitialized, is AsyncData.Loading -> ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait)) - is AsyncData.Failure -> { Timber.e(state.urlState.error, "WebView failed to load URL: ${state.urlState.error.message}") ErrorDialog( @@ -157,7 +150,6 @@ internal fun CallScreenView( onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, ) } - is AsyncData.Success -> Unit } } @@ -256,18 +248,15 @@ private fun WebView.setup( private fun WebView.addBackHandler(onBackPressed: () -> Unit) { addJavascriptInterface( - JavascriptBackHandler { - onBackPressed() + object { + @Suppress("unused") + @JavascriptInterface + fun onBackPressed() = onBackPressed() }, "backHandler" ) } -private fun WebView.dispatchEscKeyEvent() { - dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_ESCAPE)) - dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_ESCAPE)) -} - @PreviewsDayNight @Composable internal fun CallScreenViewPreview( @@ -286,8 +275,3 @@ internal fun CallScreenViewPreview( internal fun InvalidAudioDeviceDialogPreview() = ElementPreview { InvalidAudioDeviceDialog(invalidAudioDeviceReason = InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED) {} } - -internal fun interface JavascriptBackHandler { - @JavascriptInterface - fun onBackPressed() -} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt deleted file mode 100644 index f07f7039d3..0000000000 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.call.ui - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.impl.ui.CallScreenBackPressAction -import io.element.android.features.call.impl.ui.CallScreenBackPressPolicy -import org.junit.Test - -class CallScreenBackPressPolicyTest { - @Test - fun `resolve returns dispatch escape when a web view is available and native button is pressed`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = false, - hasWebView = true, - fromNative = true, - ) - - assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) - } - - @Test - fun `resolve dispatch escape when there is a web view and pip is supported on native button press`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = true, - hasWebView = true, - fromNative = true, - ) - - assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) - } - - @Test - fun `resolve returns hangup when there is no web view and pip is not supported from native button`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = false, - hasWebView = false, - fromNative = true, - ) - - assertThat(result).isNull() - } - - @Test - fun `resolve returns hangup when there is no web view even though pip is supported from native button`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = true, - hasWebView = false, - fromNative = true, - ) - - assertThat(result).isNull() - } - - @Test - fun `resolve goes to pip if its not from native but from the webview`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = true, - hasWebView = true, - fromNative = false, - ) - - assertThat(result).isEqualTo(CallScreenBackPressAction.EnterPictureInPicture) - } - @Test - fun `resolve hangs up if its not from native but from the webview and pip is not supported`() { - val result = CallScreenBackPressPolicy.resolve( - supportPip = false, - hasWebView = true, - fromNative = false, - ) - - assertThat(result).isNull() - } - - @Test - fun `invalid cases (event comes from webview but there is now webview) all result in hangup`() { - val withPipSupport = CallScreenBackPressPolicy.resolve( - supportPip = true, - hasWebView = false, - fromNative = false, - ) - assertThat(withPipSupport).isNull() - val withOutPipSupport = CallScreenBackPressPolicy.resolve( - supportPip = false, - hasWebView = false, - fromNative = false, - ) - assertThat(withOutPipSupport).isNull() - } -} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt deleted file mode 100644 index e4f9c10a3c..0000000000 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -@file:OptIn(ExperimentalTestApi::class) - -package io.element.android.features.call.ui - -import android.view.KeyEvent -import android.webkit.WebView -import androidx.activity.ComponentActivity -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.AndroidComposeUiTest -import androidx.compose.ui.test.ExperimentalTestApi -import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.element.android.features.call.impl.pip.PictureInPictureEvent -import io.element.android.features.call.impl.pip.aPictureInPictureState -import io.element.android.features.call.impl.ui.CallScreenEvent -import io.element.android.features.call.impl.ui.CallScreenView -import io.element.android.features.call.impl.ui.JavascriptBackHandler -import io.element.android.features.call.impl.ui.aCallScreenState -import io.element.android.tests.testutils.EventsRecorder -import io.element.android.tests.testutils.pressBackKey -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.annotation.Config -import org.robolectric.annotation.Implementation -import org.robolectric.annotation.Implements -import org.robolectric.annotation.Resetter -import org.robolectric.shadows.ShadowWebView - -@RunWith(AndroidJUnit4::class) -class CallScreenViewTest { - @Test - fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() = runAndroidComposeUiTest { - val callEvents = EventsRecorder() - - setCallScreenView( - state = aCallScreenState(eventSink = callEvents), - useInspectionMode = true, - ) - - pressBackKey() - - callEvents.assertEmpty() - } - - @Config(shadows = [RecordingShadowWebView::class]) - @Test - fun `pressing back key dispatches escape key events to web view when pip is unsupported`() = runAndroidComposeUiTest { - setCallScreenView( - state = aCallScreenState(), - useInspectionMode = false, - ) - - pressBackKey() - - val dispatchedEvents = RecordingShadowWebView.dispatchedEvents - assertEquals(2, dispatchedEvents.size) - assertEquals(KeyEvent.ACTION_DOWN, dispatchedEvents[0].action) - assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[0].keyCode) - assertEquals(KeyEvent.ACTION_UP, dispatchedEvents[1].action) - assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[1].keyCode) - } - - @Config(shadows = [RecordingShadowWebView::class]) - @Test - fun `web view javascript back handler emits pip event when pip is supported`() = runAndroidComposeUiTest { - val pipEvents = EventsRecorder() - - setCallScreenView( - state = aCallScreenState(), - useInspectionMode = false, - pipState = aPictureInPictureState( - supportPip = true, - eventSink = pipEvents, - ), - ) - - runOnIdle { - RecordingShadowWebView.invokeJavascriptBackHandler() - } - - pipEvents.assertSize(2) - pipEvents.assertTrue(0) { it is PictureInPictureEvent.SetPipController } - pipEvents.assertTrue(1) { it is PictureInPictureEvent.EnterPictureInPicture } - } -} - -private fun AndroidComposeUiTest.setCallScreenView( - state: io.element.android.features.call.impl.ui.CallScreenState, - useInspectionMode: Boolean, - pipState: io.element.android.features.call.impl.pip.PictureInPictureState = aPictureInPictureState(supportPip = false), -) { - setContent { - // Inspection mode disables AndroidView creation; keep it configurable per test. - CompositionLocalProvider(LocalInspectionMode provides useInspectionMode) { - CallScreenView( - state = state, - pipState = pipState, - onConsoleMessage = {}, - requestPermissions = { _, _ -> }, - ) - } - } -} - -@Implements(WebView::class) -internal class RecordingShadowWebView : ShadowWebView() { - companion object { - val dispatchedEvents = mutableListOf() - private var backHandlerJavascriptInterface: JavascriptBackHandler? = null - - @Resetter - @JvmStatic - @Suppress("unused") - fun resetRecordedEvents() { - dispatchedEvents.clear() - backHandlerJavascriptInterface = null - } - - fun invokeJavascriptBackHandler() { - val backHandler = checkNotNull(backHandlerJavascriptInterface) { "Expected backHandler JavaScript interface to be registered" } - backHandler.onBackPressed() - } - } - - @Implementation - protected override fun addJavascriptInterface(`object`: Any, name: String) { - super.addJavascriptInterface(`object`, name) - if (name == "backHandler") { - backHandlerJavascriptInterface = `object` as? JavascriptBackHandler - } - } - - @Implementation - @Suppress("unused") - fun dispatchKeyEvent(event: KeyEvent): Boolean { - dispatchedEvents += KeyEvent(event) - return false - } -} From 0a9259604bdeecfd1b943b9c867ce9bb1d5ea098 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 May 2026 15:29:32 +0200 Subject: [PATCH 084/471] Improve FakeSessionVerificationService --- .../IncomingVerificationPresenterTest.kt | 3 +++ .../OutgoingVerificationPresenterTest.kt | 12 ++++++++---- .../FakeSessionVerificationService.kt | 16 ++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) 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 index c9b9e25b74..a8d3802268 100644 --- 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 @@ -99,6 +99,7 @@ class IncomingVerificationPresenterTest { emojiState.eventSink(IncomingVerificationViewEvents.ConfirmVerification) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + advanceUntilIdle() approveVerificationLambda.assertions().isCalledOnce() // Remote confirm that the emojis match fakeSessionVerificationService.emitVerificationFlowState( @@ -161,6 +162,7 @@ class IncomingVerificationPresenterTest { emojiState.eventSink(IncomingVerificationViewEvents.DeclineVerification) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + advanceUntilIdle() declineVerificationLambda.assertions().isCalledOnce() // Remote confirm that there is a failure fakeSessionVerificationService.emitVerificationFlowState( @@ -260,6 +262,7 @@ class IncomingVerificationPresenterTest { emojiState.eventSink(IncomingVerificationViewEvents.GoBack) val emojiWaitingItem = awaitItem() assertThat((emojiWaitingItem.step as IncomingVerificationState.Step.Verifying).isWaiting).isTrue() + advanceUntilIdle() declineVerificationLambda.assertions().isCalledOnce() // Remote confirm that there is a failure fakeSessionVerificationService.emitVerificationFlowState( diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index da31b633b1..947d75dd39 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -27,6 +27,8 @@ import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -271,6 +273,7 @@ class OutgoingVerificationPresenterTest { } } + context(testScope: TestScope) private suspend fun ReceiveTurbine.requestVerificationAndAwaitVerifyingState( fakeService: FakeSessionVerificationService, sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), @@ -278,6 +281,7 @@ class OutgoingVerificationPresenterTest { var state = awaitItem() assertThat(state.step).isEqualTo(Step.Initial) state.eventSink(OutgoingVerificationViewEvents.RequestVerification) + testScope.advanceUntilIdle() // Await for other device response: fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) state = awaitItem() @@ -286,6 +290,7 @@ class OutgoingVerificationPresenterTest { state = awaitItem() assertThat(state.step).isEqualTo(Step.Ready) state.eventSink(OutgoingVerificationViewEvents.StartSasVerification) + testScope.advanceUntilIdle() // Await for other device response (again): fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) state = awaitItem() @@ -297,7 +302,7 @@ class OutgoingVerificationPresenterTest { return state } - private suspend fun unverifiedSessionService( + private fun unverifiedSessionService( requestDeviceVerificationLambda: () -> Unit = { lambdaError() }, requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() }, cancelVerificationLambda: () -> Unit = { lambdaError() }, @@ -309,6 +314,7 @@ class OutgoingVerificationPresenterTest { acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, ): FakeSessionVerificationService { return FakeSessionVerificationService( + initialSessionVerifiedStatus = SessionVerifiedStatus.NotVerified, requestDeviceVerificationLambda = requestDeviceVerificationLambda, requestUserVerificationLambda = requestUserVerificationLambda, cancelVerificationLambda = cancelVerificationLambda, @@ -318,9 +324,7 @@ class OutgoingVerificationPresenterTest { resetLambda = resetLambda, acknowledgeVerificationRequestLambda = acknowledgeVerificationRequestLambda, acceptVerificationRequestLambda = acceptVerificationRequestLambda, - ).apply { - emitVerifiedStatus(SessionVerifiedStatus.NotVerified) - } + ) } } 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 dab4d48952..0aeeebf86d 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 @@ -40,31 +40,31 @@ class FakeSessionVerificationService( override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus override val needsSessionVerification: Flow = _needsSessionVerification - override suspend fun requestDeviceVerification() { + override suspend fun requestDeviceVerification() = simulateLongTask { requestDeviceVerificationLambda() } - override suspend fun requestUserVerification(userId: UserId) { + override suspend fun requestUserVerification(userId: UserId) = simulateLongTask { requestUserVerificationLambda(userId) } - override suspend fun cancelVerification() { + override suspend fun cancelVerification() = simulateLongTask { cancelVerificationLambda() } - override suspend fun approveVerification() { + override suspend fun approveVerification() = simulateLongTask { approveVerificationLambda() } - override suspend fun declineVerification() { + override suspend fun declineVerification() = simulateLongTask { declineVerificationLambda() } - override suspend fun startSasVerification() { + override suspend fun startSasVerification() = simulateLongTask { startSasVerificationLambda() } - override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) { + override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) = simulateLongTask { resetLambda(cancelAnyPendingVerificationAttempt) } @@ -75,7 +75,7 @@ class FakeSessionVerificationService( this.listener = listener } - override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) { + override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) = simulateLongTask { acknowledgeVerificationRequestLambda(verificationRequest) } From 28e1062eed4939ca8de4a9ba9dd4370b589cff51 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Tue, 5 May 2026 17:02:52 +0300 Subject: [PATCH 085/471] Reimplement "Natural media viewer swiping order" (#6715) --- .../impl/viewer/MediaViewerDataSource.kt | 23 ++-------- .../impl/viewer/MediaViewerPresenter.kt | 22 ++++------ .../impl/viewer/MediaViewerView.kt | 1 + .../impl/viewer/MediaViewerPresenterTest.kt | 43 +++++++++---------- 4 files changed, 33 insertions(+), 56 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index 24e48531f0..eadbd544fc 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -122,25 +122,11 @@ class MediaViewerDataSource( */ private fun buildMediaViewerPageList(groupedItems: List) = buildList { // Filter out DateSeparator items, we do not need them for the media viewer - val itemsNoDateSeparator = groupedItems.filterNot { it is MediaItem.DateSeparator } - // Separate loading indicators and media events - val loadingIndicators = itemsNoDateSeparator.filterIsInstance() - val mediaEvents = itemsNoDateSeparator.filterIsInstance() - // Determine backward and forward loading indicators - val backwardLoading = loadingIndicators.find { it.direction == Timeline.PaginationDirection.BACKWARDS } - val forwardLoading = loadingIndicators.find { it.direction == Timeline.PaginationDirection.FORWARDS } - // Build ordered list: backward loading, media events (oldest first), forward loading - // Media events are currently newest first, reverse to get oldest first - val orderedEvents = mediaEvents.reversed() - // Create new list of MediaItem in order: backwardLoading, orderedEvents, forwardLoading - val orderedItems = buildList { - backwardLoading?.let { add(it) } - addAll(orderedEvents) - forwardLoading?.let { add(it) } - } - pagerKeysHandler.accept(orderedItems) - orderedItems.forEach { mediaItem -> + val groupedItemsNoDateSeparator = groupedItems.filterNot { it is MediaItem.DateSeparator } + pagerKeysHandler.accept(groupedItemsNoDateSeparator) + groupedItemsNoDateSeparator.forEach { mediaItem -> when (mediaItem) { + is MediaItem.DateSeparator -> Unit is MediaItem.Event -> { val sourceUrl = mediaItem.mediaSource().safeUrl val localMedia = localMediaStates.getOrPut(sourceUrl) { @@ -164,7 +150,6 @@ class MediaViewerDataSource( pagerKey = pagerKeysHandler.getKey(mediaItem), ) ) - is MediaItem.DateSeparator -> Unit // already filtered out } } }.toImmutableList() diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 60f03bb1e0..138c73c383 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -177,21 +177,18 @@ class MediaViewerPresenter( currentIndex: IntState, data: State>, ) { + // With newest-first ordering, backward loading indicator is at the last index val isRenderingLoadingBackward by remember { derivedStateOf { - currentIndex.intValue == 0 && + currentIndex.intValue == data.value.lastIndex && data.value.size > 1 && - data.value.firstOrNull() is MediaViewerPageData.Loading && - (data.value.firstOrNull() as? MediaViewerPageData.Loading)?.direction == Timeline.PaginationDirection.BACKWARDS + data.value.lastOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingBackward) { LaunchedEffect(Unit) { // Observe the loading data vanishing - snapshotFlow { - val first = data.value.firstOrNull() - first is MediaViewerPageData.Loading && first.direction == Timeline.PaginationDirection.BACKWARDS - } + snapshotFlow { data.value.lastOrNull() is MediaViewerPageData.Loading } .distinctUntilChanged() .filter { !it } .onEach { showNoMoreItemsSnackbar() } @@ -205,21 +202,18 @@ class MediaViewerPresenter( currentIndex: IntState, data: State>, ) { + // With newest-first ordering, forward loading indicator is at the first index val isRenderingLoadingForward by remember { derivedStateOf { - currentIndex.intValue == data.value.lastIndex && + currentIndex.intValue == 0 && data.value.size > 1 && - data.value.lastOrNull() is MediaViewerPageData.Loading && - (data.value.lastOrNull() as? MediaViewerPageData.Loading)?.direction == Timeline.PaginationDirection.FORWARDS + data.value.firstOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingForward) { LaunchedEffect(Unit) { // Observe the loading data vanishing - snapshotFlow { - val last = data.value.lastOrNull() - last is MediaViewerPageData.Loading && last.direction == Timeline.PaginationDirection.FORWARDS - } + snapshotFlow { data.value.firstOrNull() is MediaViewerPageData.Loading } .distinctUntilChanged() .filter { !it } .onEach { showNoMoreItemsSnackbar() } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 738d940453..3738f6643b 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -182,6 +182,7 @@ fun MediaViewerView( // Pre-load previous and next pages beyondViewportPageCount = 1, key = { index -> state.listData[index].pagerKey }, + reverseLayout = true, ) { page -> when (val dataForPage = state.listData[page]) { is MediaViewerPageData.Failure -> { diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index caacb03804..86689859ad 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -593,20 +593,20 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) val updatedState = awaitItem() - // User navigate to the last item (forward loading indicator) + // User navigate to the first item (forward loading indicator) updatedState.eventSink( - MediaViewerEvent.OnNavigateTo(2) + MediaViewerEvent.OnNavigateTo(0) ) // data source claims that there is no more items to load forward mediaGalleryDataSource.emitGroupedMediaItems( @@ -614,21 +614,19 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage), + fileItems = persistentListOf(anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage), + imageAndVideoItems = persistentListOf(anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) - var stateWithSnackbar = awaitItem() - while (stateWithSnackbar.snackbarMessage == null) { - stateWithSnackbar = awaitItem() - } - assertThat(stateWithSnackbar.snackbarMessage.messageResId).isEqualTo(expectedSnackbarResId) + skipItems(1) + val stateWithSnackbar = awaitItem() + assertThat(stateWithSnackbar.snackbarMessage!!.messageResId).isEqualTo(expectedSnackbarResId) } } @@ -667,42 +665,41 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) val updatedState = awaitItem() - // User navigate to the first item (backward loading indicator) + // User navigate to the last item (backward loading indicator) updatedState.eventSink( - MediaViewerEvent.OnNavigateTo(0) + MediaViewerEvent.OnNavigateTo(2) ) + skipItems(1) // data source claims that there is no more items to load backward mediaGalleryDataSource.emitGroupedMediaItems( AsyncData.Success( if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(anImage, aForwardLoadingIndicator), + fileItems = persistentListOf(aForwardLoadingIndicator, anImage), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage), fileItems = persistentListOf(), ) } ) ) - var stateWithSnackbar = awaitItem() - while (stateWithSnackbar.snackbarMessage == null) { - stateWithSnackbar = awaitItem() - } - assertThat(stateWithSnackbar.snackbarMessage.messageResId).isEqualTo(expectedSnackbarResId) + skipItems(1) + val stateWithSnackbar = awaitItem() + assertThat(stateWithSnackbar.snackbarMessage!!.messageResId).isEqualTo(expectedSnackbarResId) } } @@ -720,7 +717,7 @@ class MediaViewerPresenterTest { mediaGalleryDataSource.emitGroupedMediaItems( AsyncData.Success( GroupedMediaItems( - imageAndVideoItems = persistentListOf(aBackwardLoadingIndicator, anImage, aForwardLoadingIndicator), + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) ) From 54a66669887ffdb10511d6b2c6d57e10dea583d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:05:26 +0000 Subject: [PATCH 086/471] Update dependency net.zetetic:sqlcipher-android to v4.15.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d17ddc99d7..8fe2bde9e5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -200,7 +200,7 @@ matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } -sqlcipher = "net.zetetic:sqlcipher-android:4.14.1" +sqlcipher = "net.zetetic:sqlcipher-android:4.15.0" sqlite = "androidx.sqlite:sqlite-ktx:2.6.2" unifiedpush = "org.unifiedpush.android:connector:3.3.2" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" From 384c806131b4f679a0741c9ebd59213a30df3413 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 May 2026 16:22:32 +0200 Subject: [PATCH 087/471] Fix compilation warnings --- .../features/preferences/impl/tasks/ClearCacheUseCase.kt | 3 +-- .../features/rageshake/impl/reporter/DefaultBugReporter.kt | 3 +-- .../libraries/matrix/ui/media/ImageLoaderFactories.kt | 3 +-- .../io/element/android/libraries/network/RetrofitFactory.kt | 5 ++--- .../libraries/voicerecorder/impl/audio/DefaultEncoder.kt | 3 +-- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt index 6c26866e93..6977525548 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt @@ -11,7 +11,6 @@ package io.element.android.features.preferences.impl.tasks import android.content.Context import coil3.SingletonImageLoader import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Provider import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.preferences.impl.DefaultCacheService import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -33,7 +32,7 @@ class DefaultClearCacheUseCase( private val matrixClient: MatrixClient, private val coroutineDispatchers: CoroutineDispatchers, private val defaultCacheService: DefaultCacheService, - private val okHttpClient: Provider, + private val okHttpClient: () -> OkHttpClient, private val pushService: PushService, private val seenInvitesStore: SeenInvitesStore, private val activeRoomsHolder: ActiveRoomsHolder, diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index b4e7faa01e..2568a6fba9 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -14,7 +14,6 @@ import androidx.core.net.toFile import androidx.core.net.toUri import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Provider import dev.zacsweers.metro.SingleIn import io.element.android.appconfig.RageshakeConfig import io.element.android.features.rageshake.api.logs.createWriteToFilesConfiguration @@ -77,7 +76,7 @@ class DefaultBugReporter( private val screenshotHolder: ScreenshotHolder, private val crashDataStore: CrashDataStore, private val coroutineDispatchers: CoroutineDispatchers, - private val okHttpClient: Provider, + private val okHttpClient: () -> OkHttpClient, private val userAgentProvider: UserAgentProvider, private val sessionStore: SessionStore, private val buildMeta: BuildMeta, diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index e67e630e97..ef654a4cf7 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -16,7 +16,6 @@ import coil3.gif.GifDecoder import coil3.network.okhttp.OkHttpNetworkFetcherFactory import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Provider import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import okhttp3.OkHttpClient @@ -29,7 +28,7 @@ interface ImageLoaderFactory { @ContributesBinding(AppScope::class) class DefaultImageLoaderFactory( @ApplicationContext private val context: Context, - private val okHttpClient: Provider, + private val okHttpClient: () -> OkHttpClient, ) : ImageLoaderFactory { private val okHttpNetworkFetcherFactory = OkHttpNetworkFetcherFactory( callFactory = { diff --git a/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt b/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt index 88240c3d65..39a54dca61 100644 --- a/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt +++ b/libraries/network/src/main/kotlin/io/element/android/libraries/network/RetrofitFactory.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.network import dev.zacsweers.metro.Inject -import dev.zacsweers.metro.Provider import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.core.uri.ensureTrailingSlash import okhttp3.MediaType.Companion.toMediaType @@ -19,8 +18,8 @@ import retrofit2.converter.kotlinx.serialization.asConverterFactory @Inject class RetrofitFactory( - private val okHttpClient: Provider, - private val json: Provider, + private val okHttpClient: () -> OkHttpClient, + private val json: () -> JsonProvider, ) { fun create(baseUrl: String): Retrofit = Retrofit.Builder() .baseUrl(baseUrl.ensureTrailingSlash()) diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DefaultEncoder.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DefaultEncoder.kt index 9d471f7a6d..9d41b0da92 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DefaultEncoder.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/audio/DefaultEncoder.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.voicerecorder.impl.audio import dev.zacsweers.metro.ContributesBinding -import dev.zacsweers.metro.Provider import io.element.android.libraries.di.RoomScope import io.element.android.opusencoder.OggOpusEncoder import timber.log.Timber @@ -20,7 +19,7 @@ import java.io.File */ @ContributesBinding(RoomScope::class) class DefaultEncoder( - private val encoderProvider: Provider, + private val encoderProvider: () -> OggOpusEncoder, config: AudioConfig, ) : Encoder { private val bitRate = config.bitRate From 3b3b4813b685a3fa94126fbe8b6429d8c334467c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 May 2026 16:50:22 +0200 Subject: [PATCH 088/471] Detekt and ktlint are confused with Kotlin context... --- .../impl/outgoing/OutgoingVerificationPresenterTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt index 947d75dd39..c6cbfca281 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationPresenterTest.kt @@ -6,6 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ +@file:Suppress("UnusedImports") + package io.element.android.features.verifysession.impl.outgoing import app.cash.turbine.ReceiveTurbine From a7711b7d524c4c8b1592aac5c4c3cbaeba24fa06 Mon Sep 17 00:00:00 2001 From: Kurban Sagitov <58472509+krbns@users.noreply.github.com> Date: Tue, 5 May 2026 18:10:24 +0300 Subject: [PATCH 089/471] Fix low width image message (#6692) * fix low width image message * added previews for narrow images * Update screenshots --------- Co-authored-by: ElementBot --- .../components/event/TimelineItemImageView.kt | 47 ++++++++++++++++++- .../event/TimelineItemImageContentProvider.kt | 6 ++- ...s.event_ATimelineItemEventRow_Day_0_en.png | 3 ++ ...event_ATimelineItemEventRow_Night_0_en.png | 3 ++ 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png 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 bef06bcd73..0e0a98a96c 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 @@ -58,6 +58,7 @@ import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.wysiwyg.compose.EditorStyledText import io.element.android.wysiwyg.link.Link +private const val TALL_IMAGE_RATIO_DIVISOR = 3 @Composable fun TimelineItemImageView( content: TimelineItemImageContent, @@ -79,7 +80,7 @@ fun TimelineItemImageView( Modifier } TimelineItemAspectRatioBox( - modifier = containerModifier.blurHashBackground(content.blurhash, alpha = 0.9f), + modifier = containerModifier.blurHashBackground(content.blurhash, alpha = 0.9f).align(Alignment.CenterHorizontally), aspectRatio = coerceRatioWhenHidingContent(content.aspectRatio, hideMediaContent), ) { ProtectedView( @@ -123,7 +124,14 @@ fun TimelineItemImageView( LocalContentColor provides ElementTheme.colors.textPrimary, LocalTextStyle provides ElementTheme.typography.fontBodyLgRegular ) { - val aspectRatio = content.aspectRatio ?: DEFAULT_ASPECT_RATIO + val width = content.width ?: 0 + val height = content.height ?: 0 + // if image is narrow and tall use DEFAULT_ASPECT_RATIO + val aspectRatio = if (width < height / TALL_IMAGE_RATIO_DIVISOR) { + DEFAULT_ASPECT_RATIO + } else { + content.aspectRatio ?: DEFAULT_ASPECT_RATIO + } EditorStyledText( modifier = Modifier .padding(horizontal = 4.dp) // This is (12.dp - 8.dp) contentPadding from CommonLayout @@ -200,3 +208,38 @@ internal fun TimelineImageWithCaptionRowPreview() = ElementPreview { ) } } + +@PreviewsDayNight +@Composable +internal fun ATimelineItemEventRowPreview() = ElementPreview { + Column { + sequenceOf(false, true).forEach { isMine -> + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = isMine, + content = aTimelineItemImageContent( + filename = "image.jpg", + caption = "A long caption that may wrap into several lines", + width = 80, + height = 300, + aspectRatio = 80f / 300f, + ), + groupPosition = TimelineItemGroupPosition.Last, + ), + ) + } + ATimelineItemEventRow( + event = aTimelineItemEvent( + isMine = false, + content = aTimelineItemImageContent( + filename = "image.jpg", + caption = "Narrow image with null aspectRatio", + width = 80, + height = 300, + aspectRatio = null, + ), + groupPosition = TimelineItemGroupPosition.Last, + ), + ) + } +} 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 d07d5db6a4..6d3b114428 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 @@ -28,6 +28,8 @@ fun aTimelineItemImageContent( blurhash: String? = A_BLUR_HASH, filename: String = "A picture.jpg", caption: String? = null, + width: Int? = null, + height: Int? = 300, ) = TimelineItemImageContent( filename = filename, fileSize = 4 * 1024 * 1024L, @@ -38,8 +40,8 @@ fun aTimelineItemImageContent( thumbnailSource = null, mimeType = MimeTypes.IMAGE_JPEG, blurhash = blurhash, - width = null, - height = 300, + width = width, + height = height, thumbnailWidth = null, thumbnailHeight = 150, aspectRatio = aspectRatio, diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png new file mode 100644 index 0000000000..08169845d5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86eac241e9084cc830ff13d231908da2eb0dff246aa5cac64dddaac68a5d5f13 +size 295298 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png new file mode 100644 index 0000000000..06dd93b269 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:578a3707102c7754f607c3a14f94f32cafa30ee570db174386533c484cb4773c +size 295031 From 47ca5a5f2fed5510e198085e10529785c19afae1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 01:36:24 +0000 Subject: [PATCH 090/471] Update dependency org.maplibre.gl:android-sdk to v13.1.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d17ddc99d7..5b99d65539 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -207,7 +207,7 @@ vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil3", 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:13.0.2" +maplibre = "org.maplibre.gl:android-sdk:13.1.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" maplibre_compose = "org.maplibre.compose:maplibre-compose:0.12.1" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" From 70452842d31e6e037ae32ed4a29c583f0d8bf6c1 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 6 May 2026 11:16:13 +0200 Subject: [PATCH 091/471] Make icons in the Chat screen top bar 16dp (#6733) * Make icons in the Chat screen top bar 16dp. This matches the designs in Figma. * Fix the padding between the title and the icons * Update screenshots --------- Co-authored-by: ElementBot --- .../features/messages/impl/topbars/MessagesViewTopBar.kt | 9 ++++++++- ...messages.impl.topbars_MessagesViewTopBar_Day_0_en.png | 4 ++-- ...ssages.impl.topbars_MessagesViewTopBar_Night_0_en.png | 4 ++-- .../features.messages.impl_MessagesViewA11y_en.png | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt index 4d7242ebf5..b1a31da443 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable @@ -91,9 +92,12 @@ internal fun MessagesViewTopBar( modifier = titleModifier ) + val iconModifier = Modifier.size(16.dp) + when (dmUserIdentityState) { IdentityState.Verified -> { Icon( + modifier = iconModifier, imageVector = CompoundIcons.Verified(), tint = ElementTheme.colors.iconSuccessPrimary, contentDescription = null, @@ -101,6 +105,7 @@ internal fun MessagesViewTopBar( } IdentityState.VerificationViolation -> { Icon( + modifier = iconModifier, imageVector = CompoundIcons.ErrorSolid(), tint = ElementTheme.colors.iconCriticalPrimary, contentDescription = null, @@ -112,11 +117,13 @@ internal fun MessagesViewTopBar( when (sharedHistoryIcon) { SharedHistoryIcon.NONE -> Unit SharedHistoryIcon.SHARED -> Icon( + modifier = iconModifier, imageVector = CompoundIcons.History(), tint = ElementTheme.colors.iconInfoPrimary, contentDescription = stringResource(CommonStrings.common_shared_history), ) SharedHistoryIcon.WORLD_READABLE -> Icon( + modifier = iconModifier, imageVector = CompoundIcons.UserProfileSolid(), tint = ElementTheme.colors.iconInfoPrimary, contentDescription = stringResource(CommonStrings.common_world_readable_history), @@ -150,7 +157,7 @@ private fun RoomAvatarAndNameRow( ) Text( modifier = Modifier - .padding(horizontal = 8.dp) + .padding(start = 8.dp) .semantics { heading() }, diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png index b2c131b124..68e8a2127e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e26887bd81e10726414e1833029b4b51e22e534684a230777ca50f86024af994 -size 56430 +oid sha256:dc098d35a0af82ec65dee8ccadf6df83246fbbc7933239fd940de938a4dd4476 +size 55995 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png index 41b8b16bcc..13207941ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_MessagesViewTopBar_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce44cf850169736008a3f3fc21a2be4fb044badfae631b0284ce379b325879df -size 55533 +oid sha256:fb5305c7ccf69c49fde95cd3601bf2ee103988775b366cb2ee3fcd04e8673729 +size 55131 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png index 59b336cb1a..c214011ea7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c64b270815016ecd157fe430538090afc8f94c4ec2402a6c6de01ff1d762db94 -size 131744 +oid sha256:23aabf96e5dc84257968a77ad406a8f3952b24fa8d8c03612bddfc08b6c990c6 +size 131751 From 34f001d22ce2c09f8c983bc7795d866067aacac8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 6 May 2026 14:35:08 +0200 Subject: [PATCH 092/471] Fix back button sometimes not working after exiting a thread (#6732) When I reproduced the issue, it looked like the coroutine used to mark as read and then exit the room was canceled, leaving the `markingAsReadAndExiting` variable with `true` value and preventing the exit block from running again. --- .../element/android/features/messages/impl/MessagesPresenter.kt | 2 ++ 1 file changed, 2 insertions(+) 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 b98bd764de..5271906fff 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 @@ -272,6 +272,8 @@ class MessagesPresenter( } } navigator.close() + }.invokeOnCompletion { + markingAsReadAndExiting.set(false) } } } From f4cf704335c3a1abbcada78c0d84bc236ba13599 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 15:31:52 +0200 Subject: [PATCH 093/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6 (#6734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6 * Fix API breaks: - Add `RoomMember.isServiceMember`. - Add `beacon` and `beaconInfo` power levels. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .../features/createroom/impl/ConfigureRoomPresenterTest.kt | 4 +++- .../impl/permissions/ChangeRoomPermissionsStateProvider.kt | 3 +++ .../impl/permissions/ChangeRoomPermissionsPresenterTest.kt | 4 +++- gradle/libs.versions.toml | 2 +- .../matrix/api/room/powerlevels/RoomPowerLevelsValues.kt | 2 ++ .../impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt | 2 ++ .../libraries/matrix/impl/fixtures/factories/RoomMember.kt | 2 ++ .../matrix/impl/fixtures/factories/RoomPowerLevelsValues.kt | 6 +++++- .../matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt | 2 ++ .../room/powerlevels/RoomPowerLevelsValuesMapperTest.kt | 4 ++++ .../android/libraries/matrix/test/room/FakeBaseRoom.kt | 2 ++ 11 files changed, 29 insertions(+), 4 deletions(-) diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt index fcedcb2367..b9d7eb6e7d 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/ConfigureRoomPresenterTest.kt @@ -301,7 +301,9 @@ class ConfigureRoomPresenterTest { roomName = 0, roomAvatar = 0, roomTopic = 0, - spaceChild = 0 + spaceChild = 0, + beacon = 0, + beaconInfo = 0, ), users = persistentMapOf(), ) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt index 2760272d8a..700a82795e 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsStateProvider.kt @@ -63,5 +63,8 @@ private fun previewPermissions(): RoomPowerLevelsValues { // SpaceManagement section spaceChild = RoomMember.Role.Moderator.powerLevel, stateDefault = RoomMember.Role.Moderator.powerLevel, + // Live location beacon section + beacon = RoomMember.Role.Admin.powerLevel, + beaconInfo = RoomMember.Role.Moderator.powerLevel, ) } diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt index ba7d47adb2..72b2b8b5e7 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt @@ -148,7 +148,9 @@ class ChangeRoomPermissionsPresenterTest { roomName = Moderator.powerLevel, roomAvatar = Moderator.powerLevel, roomTopic = Moderator.powerLevel, - spaceChild = initialPermissions.spaceChild + spaceChild = initialPermissions.spaceChild, + beacon = initialPermissions.beacon, + beaconInfo = initialPermissions.beaconInfo, ) ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d4fbe6827..c03d164728 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.4" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.6" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt index e8f88ed86d..6606465389 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevelsValues.kt @@ -19,4 +19,6 @@ data class RoomPowerLevelsValues( val roomAvatar: Long, val roomTopic: Long, val spaceChild: Long, + val beacon: Long, + val beaconInfo: Long, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt index 5e2a1c82da..499868795a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt @@ -26,6 +26,8 @@ object RoomPowerLevelsValuesMapper { roomAvatar = values.roomAvatar, roomTopic = values.roomTopic, spaceChild = values.spaceChild, + beacon = values.beacon, + beaconInfo = values.beaconInfo, ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt index 77fa814cca..6b4f958d84 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt @@ -24,6 +24,7 @@ internal fun aRustRoomMember( isIgnored: Boolean = false, role: RoomMemberRole = RoomMemberRole.USER, membershipChangeReason: String? = null, + isServiceMember: Boolean = false, ) = RoomMember( userId = userId.value, displayName = displayName, @@ -34,4 +35,5 @@ internal fun aRustRoomMember( isIgnored = isIgnored, suggestedRoleForPowerLevel = role, membershipChangeReason = membershipChangeReason, + isServiceMember = isServiceMember, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomPowerLevelsValues.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomPowerLevelsValues.kt index 1c1bbb42e3..28af0093f6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomPowerLevelsValues.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomPowerLevelsValues.kt @@ -22,6 +22,8 @@ internal fun aRustRoomPowerLevelsValues( roomAvatar: Long, roomTopic: Long, spaceChild: Long, + beacon: Long, + beaconInfo: Long, ) = RoomPowerLevelsValues( ban = ban, invite = invite, @@ -33,5 +35,7 @@ internal fun aRustRoomPowerLevelsValues( roomName = roomName, roomAvatar = roomAvatar, roomTopic = roomTopic, - spaceChild = spaceChild + spaceChild = spaceChild, + beacon = beacon, + beaconInfo = beaconInfo, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt index c47c4406b6..985dc935ff 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiRoomPowerLevels.kt @@ -32,4 +32,6 @@ fun defaultFfiRoomPowerLevelValues() = RoomPowerLevelsValues( roomTopic = 50, spaceChild = 50, usersDefault = 0, + beacon = 0, + beaconInfo = 0, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt index f298da8b42..65f2d1e2d5 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapperTest.kt @@ -30,6 +30,8 @@ class RoomPowerLevelsValuesMapperTest { roomAvatar = 9, roomTopic = 10, spaceChild = 11, + beacon = 12, + beaconInfo = 13, ) ) ).isEqualTo( @@ -44,6 +46,8 @@ class RoomPowerLevelsValuesMapperTest { roomAvatar = 9, roomTopic = 10, spaceChild = 11, + beacon = 12, + beaconInfo = 13, ) ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt index 4ceddc414c..5ea2de888b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt @@ -223,4 +223,6 @@ fun defaultRoomPowerLevelValues() = RoomPowerLevelsValues( roomAvatar = 50, roomTopic = 50, spaceChild = 50, + beacon = 0, + beaconInfo = 0, ) From 9b91fabbd699d69107d1378c6b7f34f17f462d87 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 6 May 2026 15:33:17 +0200 Subject: [PATCH 094/471] Use 'Report a problem' string instead of 'Report bug' (#6735) * Use 'Report a problem' instead of 'Report bug'. This old string will be deleted soon. * Update screenshots --------- Co-authored-by: ElementBot --- .../features/rageshake/api/crash/CrashDetectionView.kt | 2 +- .../rageshake/api/detection/RageshakeDetectionView.kt | 2 +- .../test/snapshots/images/appnav.root_RootView_Day_0_en.png | 4 ++-- .../test/snapshots/images/appnav.root_RootView_Day_1_en.png | 4 ++-- .../test/snapshots/images/appnav.root_RootView_Night_0_en.png | 4 ++-- .../test/snapshots/images/appnav.root_RootView_Night_1_en.png | 4 ++-- ...atures.rageshake.api.crash_CrashDetectionView_Day_0_en.png | 4 ++-- ...ures.rageshake.api.crash_CrashDetectionView_Night_0_en.png | 4 ++-- ...ageshake.api.detection_RageshakeDialogContent_Day_0_en.png | 4 ++-- ...eshake.api.detection_RageshakeDialogContent_Night_0_en.png | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt index 5db1d4f076..ea78ed5a2c 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/crash/CrashDetectionView.kt @@ -43,7 +43,7 @@ private fun CrashDetectionContent( onDismiss: () -> Unit = { }, ) { ConfirmationDialog( - title = stringResource(id = CommonStrings.action_report_bug), + title = stringResource(id = CommonStrings.common_report_a_problem), content = stringResource(id = R.string.crash_detection_dialog_content, appName), submitText = stringResource(id = CommonStrings.action_yes), cancelText = stringResource(id = CommonStrings.action_no), diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt index 745a362637..c60bd4e1bf 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionView.kt @@ -77,7 +77,7 @@ private fun RageshakeDialogContent( onYesClick: () -> Unit = { }, ) { ConfirmationDialog( - title = stringResource(id = CommonStrings.action_report_bug), + title = stringResource(id = CommonStrings.common_report_a_problem), content = stringResource(id = R.string.rageshake_detection_dialog_content), thirdButtonText = stringResource(id = CommonStrings.action_disable), submitText = stringResource(id = CommonStrings.action_yes), diff --git a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png index 8ab5f8a44a..559c023aa7 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d225730fee6fae0d93396a6c47dcd7abd86ba8902ac521c96e05fa36f619bbd -size 25840 +oid sha256:ba67fa9a541371f84cdc16dcc0c069e0cfda486c2b21dbb885cee7e0d9b6b005 +size 26924 diff --git a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png index e2037ca626..788daaf8df 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f27f94a461f5b4bc5289ac41691e3736a73ddcbb9e1f2a1d0c9390a0ce5cb44b -size 27919 +oid sha256:00f8ec369fa64c6bbd97b37a5b53481438e74abdb5d366d32e3acae29460659c +size 29075 diff --git a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png index 9df8fe1585..6ac6642b35 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:708a157d8a7cb5da2b2ea52c12f9b10c172ec380dfcec09583adfe2ea4af3d74 -size 24610 +oid sha256:0a38ade8b9a5e3ce85fbe63ae76d2e856ac46c6cdc012a4952cb191fcbe8979f +size 25577 diff --git a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png index 00e1659da8..5451e763d5 100644 --- a/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/appnav.root_RootView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:260ea7d9f891afdea78a6d546138cbad7586fe2210058d3774d549c9918c2ccd -size 26529 +oid sha256:1ae9d78458ea0b9d4b258be0620698abb699bcbc70275d98adfd992d8d50ec59 +size 27541 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Day_0_en.png index 1dcb5f4f8f..3c898e8ab5 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc7a2e36694227df123681799f09bb2ee3dae24258679557193ae69ecf4c2871 -size 24012 +oid sha256:17a23d0c9d2993b81edb72f19ba6ec03f24b79a2f732e7af6d3594c96503723c +size 24978 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Night_0_en.png index 65a0322a88..b3f17cafcb 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.api.crash_CrashDetectionView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18bbb07caa3cae740bb80297ced37fc3e8c92b48cd0935f716a86e0664737a75 -size 22815 +oid sha256:e2d914dbf6a309cffcba36f0affecf19287ad06c751f50b23a51e9df23bfd12e +size 23859 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Day_0_en.png index 77b4b1e222..b8fb7eb092 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31a1989c17263d25d5bde65a7b0399b58c9e77d320654ac9f0e65f0fe2410b15 -size 25882 +oid sha256:eea615f75cb1c3db04cffba9798f6662376f1c1db6825aa73c2db6f10192d23d +size 26959 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Night_0_en.png index d8fdf936f2..5bc6eeb700 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.api.detection_RageshakeDialogContent_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28f410c8068e0d7586af4c727ce5ba131e56387472f661b34841139e1e43cd3d -size 24788 +oid sha256:56c84d3a772d03eb016807b886df9728a5fbaf07e3fc6dc2ce4353007aec405a +size 25726 From 6ef9315468e38445f6b6a5a8d13a5e66ba0cfb6f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 16:05:19 +0200 Subject: [PATCH 095/471] =?UTF-8?q?Remove=20RoomDirectorySearch=20feature?= =?UTF-8?q?=20flag=20=E2=80=94=20always=20enable=20the=20feature=20(#6736)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove RoomDirectorySearch feature flag, always enable the feature Co-authored-by: stefanceriu <637564+stefanceriu@users.noreply.github.com> * Apply ktlint formatting Co-authored-by: jmartinesp <480955+jmartinesp@users.noreply.github.com> * Update screenshots --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: stefanceriu <637564+stefanceriu@users.noreply.github.com> Co-authored-by: jmartinesp <480955+jmartinesp@users.noreply.github.com> Co-authored-by: ElementBot --- .../AppDeveloperSettingsPresenter.kt | 12 ----------- .../AppDeveloperSettingsPresenterTest.kt | 18 ----------------- .../startchat/impl/root/StartChatPresenter.kt | 10 ---------- .../startchat/impl/root/StartChatState.kt | 1 - .../impl/root/StartChatStateProvider.kt | 5 ----- .../startchat/impl/root/StartChatView.kt | 14 ++++++------- .../impl/root/StartChatPresenterTest.kt | 20 ------------------- .../startchat/impl/root/StartChatViewTest.kt | 1 - .../libraries/featureflag/api/FeatureFlags.kt | 7 ------- ...tchat.impl.root_StartChatView_Day_0_en.png | 4 ++-- ...tchat.impl.root_StartChatView_Day_3_en.png | 4 ++-- ...tchat.impl.root_StartChatView_Day_4_en.png | 4 ++-- ...tchat.impl.root_StartChatView_Day_5_en.png | 3 --- ...hat.impl.root_StartChatView_Night_0_en.png | 4 ++-- ...hat.impl.root_StartChatView_Night_3_en.png | 4 ++-- ...hat.impl.root_StartChatView_Night_4_en.png | 4 ++-- ...hat.impl.root_StartChatView_Night_5_en.png | 3 --- 17 files changed, 18 insertions(+), 100 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_5_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_5_en.png diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt index 4c76c6ec7e..e7ad6e2284 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt @@ -25,10 +25,7 @@ import io.element.android.features.rageshake.api.preferences.RageshakePreference import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.collections.immutable.ImmutableList @@ -45,7 +42,6 @@ class AppDeveloperSettingsPresenter( private val featureFlagService: FeatureFlagService, private val rageshakePresenter: Presenter, private val appPreferencesStore: AppPreferencesStore, - private val buildMeta: BuildMeta, ) : Presenter { @Composable override fun present(): AppDeveloperSettingsState { @@ -71,14 +67,6 @@ class AppDeveloperSettingsPresenter( LaunchedEffect(Unit) { featureFlagService.getAvailableFeatures() - .run { - // Never display room directory search in release builds for Play Store - if (buildMeta.flavorDescription == "GooglePlay" && buildMeta.buildType == BuildType.RELEASE) { - filterNot { it.key == FeatureFlags.RoomDirectorySearch.key } - } else { - this - } - } .forEach { feature -> enabledFeatures.add(EnabledFeature(feature, featureFlagService.isFeatureEnabled(feature))) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt index 0e9d774e84..3d2cc85e54 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt @@ -13,13 +13,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.featureflag.api.Feature -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.featureflag.test.FakeFeatureFlagService -import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -68,18 +64,6 @@ class AppDeveloperSettingsPresenterTest { } } - @Test - fun `present - ensures Room directory search is not present on release Google Play builds`() = runTest { - val buildMeta = aBuildMeta(buildType = BuildType.RELEASE, flavorDescription = "GooglePlay") - val presenter = createAppDeveloperSettingsPresenter(buildMeta = buildMeta) - presenter.test { - skipItems(1) - awaitItem().also { state -> - assertThat(state.features).doesNotContain(FeatureFlags.RoomDirectorySearch) - } - } - } - @Test fun `present - ensures state is updated when enabled feature event is triggered`() = runTest { val presenter = createAppDeveloperSettingsPresenter() @@ -156,13 +140,11 @@ class AppDeveloperSettingsPresenterTest { } ), preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), - buildMeta: BuildMeta = aBuildMeta(), ): AppDeveloperSettingsPresenter { return AppDeveloperSettingsPresenter( featureFlagService = featureFlagService, rageshakePresenter = { aRageshakePreferencesState() }, appPreferencesStore = preferencesStore, - buildMeta = buildMeta, ) } } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt index e176f202ad..079ece4180 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenter.kt @@ -10,8 +10,6 @@ package io.element.android.features.startchat.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -24,8 +22,6 @@ import io.element.android.features.startchat.impl.userlist.UserListPresenterArgs import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.usersearch.api.UserRepository import kotlinx.coroutines.launch @@ -37,7 +33,6 @@ class StartChatPresenter( userListDataStore: UserListDataStore, private val startDMAction: StartDMAction, private val buildMeta: BuildMeta, - private val featureFlagService: FeatureFlagService, ) : Presenter { private val presenter = presenterFactory.create( UserListPresenterArgs( @@ -54,10 +49,6 @@ class StartChatPresenter( val localCoroutineScope = rememberCoroutineScope() val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - val isRoomDirectorySearchEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.RoomDirectorySearch) - }.collectAsState(initial = false) - fun handleEvent(event: StartChatEvents) { when (event) { is StartChatEvents.StartDM -> localCoroutineScope.launch { @@ -75,7 +66,6 @@ class StartChatPresenter( applicationName = buildMeta.applicationName, userListState = userListState, startDmAction = startDmActionState.value, - isRoomDirectorySearchEnabled = isRoomDirectorySearchEnabled, eventSink = ::handleEvent, ) } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt index 65f977d3e3..e6746e1302 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatState.kt @@ -16,6 +16,5 @@ data class StartChatState( val applicationName: String, val userListState: UserListState, val startDmAction: AsyncAction, - val isRoomDirectorySearchEnabled: Boolean, val eventSink: (StartChatEvents) -> Unit, ) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt index a1e8f9d4f0..41a3551d1d 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatStateProvider.kt @@ -55,9 +55,6 @@ open class StartChatStateProvider : PreviewParameterProvider { aCreateRoomRootState( startDmAction = aConfirmingStartDmWithMatrixUser() ), - aCreateRoomRootState( - isRoomDirectorySearchEnabled = true, - ), ) } @@ -75,12 +72,10 @@ fun aCreateRoomRootState( applicationName: String = "Element X Preview", userListState: UserListState = aUserListState(), startDmAction: AsyncAction = AsyncAction.Uninitialized, - isRoomDirectorySearchEnabled: Boolean = false, eventSink: (StartChatEvents) -> Unit = {}, ) = StartChatState( applicationName = applicationName, userListState = userListState, startDmAction = startDmAction, - isRoomDirectorySearchEnabled = isRoomDirectorySearchEnabled, eventSink = eventSink, ) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt index e077cbe82e..7537fee524 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatView.kt @@ -176,14 +176,12 @@ private fun CreateRoomActionButtonsList( onClick = onNewRoomClick, ) } - if (state.isRoomDirectorySearchEnabled) { - item { - CreateRoomActionButton( - iconRes = CompoundDrawables.ic_compound_list_bulleted, - text = stringResource(id = R.string.screen_room_directory_search_title), - onClick = onRoomDirectorySearchClick, - ) - } + item { + CreateRoomActionButton( + iconRes = CompoundDrawables.ic_compound_list_bulleted, + text = stringResource(id = R.string.screen_room_directory_search_title), + onClick = onRoomDirectorySearchClick, + ) } item { CreateRoomActionButton( diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt index 2bc15e989d..57f6cd2333 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt @@ -17,8 +17,6 @@ import io.element.android.features.startchat.impl.userlist.FakeUserListPresenter import io.element.android.features.startchat.impl.userlist.FakeUserListPresenterFactory import io.element.android.features.startchat.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.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser @@ -155,34 +153,16 @@ class StartChatPresenterTest { ) } } - - @Test - fun `present - room directory search`() = runTest { - val presenter = createStartChatPresenter(isRoomDirectorySearchEnabled = true) - presenter.test { - skipItems(1) - awaitItem().let { state -> - assertThat(state.isRoomDirectorySearchEnabled).isTrue() - } - } - } } internal fun createStartChatPresenter( startDMAction: StartDMAction = FakeStartDMAction(), - isRoomDirectorySearchEnabled: Boolean = false, ): StartChatPresenter { - val featureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.RoomDirectorySearch.key to isRoomDirectorySearchEnabled, - ), - ) return StartChatPresenter( presenterFactory = FakeUserListPresenterFactory(FakeUserListPresenter()), userRepository = FakeUserRepository(), userListDataStore = UserListDataStore(), startDMAction = startDMAction, - featureFlagService = featureFlagService, buildMeta = aBuildMeta(), ) } diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt index abcb70113b..916611bf20 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatViewTest.kt @@ -123,7 +123,6 @@ class StartChatViewTest { setStartChatView( aCreateRoomRootState( eventSink = eventsRecorder, - isRoomDirectorySearchEnabled = true ), onRoomDirectorySearchClick = it ) 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 15e61f4260..cdda66579b 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 @@ -22,13 +22,6 @@ enum class FeatureFlags( override val isFinished: Boolean, override val isInLabs: Boolean = false, ) : Feature { - RoomDirectorySearch( - key = "feature.roomdirectorysearch", - title = "Room directory search", - description = "Allow user to search for public rooms in their homeserver", - defaultValue = { false }, - isFinished = false, - ), ShowBlockedUsersDetails( key = "feature.showBlockedUsersDetails", title = "Show blocked users details", diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_0_en.png index ad21975ebd..a95c48fec0 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c745de02098d9e38f75fa7c34f436ad4f7770f4d0cbab03949e0f3c6bfc958 -size 25557 +oid sha256:1d22817145519d17475a09dbe9f5e3a71ec6b5ff9e917d7a92b06feb3ba865c9 +size 29049 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_3_en.png index 815a4f9ed5..d0d1748a80 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76947cf0ea5358646af7726987bd3cd3c49694aceba9850bb0e60a63f3fe669d -size 49542 +oid sha256:2f2bf708bff2bf2a2581c0eb0adfa63b38f0fd5f5fda214594049c2b40a98e09 +size 48731 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png index 920ac350fe..c2b61f4018 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf3add28f315b5f7de06dbdf5b6d5fb96552f648a95a620e50b65fa970de32dd -size 41513 +oid sha256:657e469b19b815ad54ef7ccd6a140d33031301de504690cb520c482f469fa8c4 +size 38559 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_5_en.png deleted file mode 100644 index a95c48fec0..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d22817145519d17475a09dbe9f5e3a71ec6b5ff9e917d7a92b06feb3ba865c9 -size 29049 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_0_en.png index 8071ebd4ea..8e03ad6ce9 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4c1e564d2cdb6f6a61a02274d63e2610c9b75f7f53c36b473716bba2400798e -size 24752 +oid sha256:bb277b825fefc001450c45a0786afce546f2e0cf7be878ed7bcf401c501c2431 +size 27988 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_3_en.png index ef1431db80..7aea3012b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:884e301844c610497bf5b5d52445e8124e9eaafd591853a55ab72175096942ab -size 49336 +oid sha256:de50204e7681fead39e7a8f298149b2657a3663197ddc1cfacce4389641f74f5 +size 48222 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png index 6b225ffae0..cc73f71d0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81be06688e135d746d30140e61aa168f0d7bfecad1a12e54d9d26e3eb55be0be -size 39897 +oid sha256:02a03b83eaf866a7a65ec26f57fa810a08ad7128d11d2db6ad203bf405a1bb05 +size 37097 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_5_en.png deleted file mode 100644 index 8e03ad6ce9..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_5_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb277b825fefc001450c45a0786afce546f2e0cf7be878ed7bcf401c501c2431 -size 27988 From 2a694f6dfdc8b4ce5af3410fc637eed674b6ddf2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 17:44:37 +0200 Subject: [PATCH 096/471] Create PreviewData with sample of UGC used for preview. Fix preview issue where username was used for room/avatar name. --- .../impl/ui/CallNotificationDataProvider.kt | 6 ++- .../impl/model/RoomListRoomSummaryProvider.kt | 20 +++++---- .../AcceptDeclineInviteStateProvider.kt | 5 ++- .../impl/DefaultInvitePeopleStateProvider.kt | 33 +++++++++------ .../joinroom/impl/JoinRoomStateProvider.kt | 11 +++-- .../KnockRequestsBannerStateProvider.kt | 11 +++-- .../impl/show/ShowLocationStateProvider.kt | 3 +- .../LoginWithClassicStateProvider.kt | 3 +- .../messages/impl/MessagesStateProvider.kt | 5 ++- .../identity/IdentityChangeStateProvider.kt | 3 +- ...lveVerifiedUserSendFailureStateProvider.kt | 5 ++- .../impl/timeline/TimelineStateProvider.kt | 6 ++- .../typing/TypingNotificationStateProvider.kt | 27 +++++++----- .../impl/roles/ChangeRolesStateProvider.kt | 18 ++++---- .../members/RoomMemberListStateProvider.kt | 27 ++++++++---- ...ternalRoomMemberModerationStateProvider.kt | 5 ++- .../impl/leave/LeaveSpaceStateProvider.kt | 3 +- .../settings/SpaceSettingsStateProvider.kt | 3 +- .../IncomingVerificationStateProvider.kt | 5 ++- .../components/avatar/AvatarDataProvider.kt | 6 ++- .../designsystem/preview/PreviewData.kt | 26 ++++++++++++ .../ui/components/MatrixUserProvider.kt | 41 ++++++++++++------- .../matrix/ui/components/SelectedUser.kt | 3 +- .../matrix/ui/components/SpaceHeaderView.kt | 12 ++++-- .../matrix/ui/components/SpaceMembersView.kt | 12 ++++-- .../matrix/ui/components/SpaceRoomProvider.kt | 10 +++-- .../reply/InReplyToDetailsProvider.kt | 3 +- ...tomSheetStateDeleteConfirmationProvider.kt | 3 +- .../MediaBottomSheetStateDetailsProvider.kt | 3 +- .../impl/gallery/MediaGalleryStateProvider.kt | 3 +- .../impl/viewer/MediaViewerStateProvider.kt | 3 +- 31 files changed, 214 insertions(+), 110 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewData.kt diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt index 3a51a014df..9e551b3e1b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallNotificationDataProvider.kt @@ -9,6 +9,8 @@ package io.element.android.features.call.impl.ui import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.call.impl.notifications.CallNotificationData +import io.element.android.libraries.designsystem.preview.ROOM_NAME +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId @@ -34,8 +36,8 @@ internal fun aCallNotificationData( roomId = RoomId("!1234:matrix.org"), eventId = EventId("\$asdadadsad:matrix.org"), senderId = UserId("@bob:matrix.org"), - roomName = "A room", - senderName = "Bob", + roomName = ROOM_NAME, + senderName = USER_NAME_BOB, avatarUrl = null, notificationChannelId = "incoming_call", timestamp = 0L, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt index eefb2d6484..09e6c2e6c9 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/model/RoomListRoomSummaryProvider.kt @@ -11,6 +11,10 @@ package io.element.android.features.home.impl.model import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.LAST_MESSAGE +import io.element.android.libraries.designsystem.preview.ROOM_NAME +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId @@ -85,16 +89,16 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider { @@ -26,7 +27,7 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider { joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned( banSender = InviteSender( userId = UserId("@alice:domain"), - displayName = "Alice", - avatarData = AvatarData("alice", "Alice", size = AvatarSize.InviteSender), + displayName = USER_NAME_ALICE, + avatarData = AvatarData("alice", USER_NAME_ALICE, size = AvatarSize.InviteSender), membershipChangeReason = "spamming" ), reason = "spamming", @@ -222,7 +225,7 @@ fun aJoinRoomState( internal fun anInviteSender( userId: UserId = UserId("@bob:domain"), - displayName: String = "Bob", + displayName: String = USER_NAME_BOB, avatarData: AvatarData = AvatarData(userId.value, displayName, size = AvatarSize.InviteSender), membershipChangeReason: String? = null, ) = InviteSender( @@ -234,7 +237,7 @@ internal fun anInviteSender( internal fun anInviteData( roomId: RoomId = A_ROOM_ID, - roomName: String = "Room name", + roomName: String = ROOM_NAME, isDm: Boolean = false, ) = InviteData( roomId = roomId, diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt index 67f1aaae8f..7210e783fe 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerStateProvider.kt @@ -11,6 +11,9 @@ package io.element.android.features.knockrequests.impl.banner import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.knockrequests.impl.data.KnockRequestPresentable import io.element.android.features.knockrequests.impl.data.aKnockRequestPresentable +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE import kotlinx.collections.immutable.toImmutableList class KnockRequestsBannerStateProvider : PreviewParameterProvider { @@ -29,15 +32,15 @@ class KnockRequestsBannerStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoginWithClassicState(), - aLoginWithClassicState(isElementPro = true, displayName = "Alice"), + aLoginWithClassicState(isElementPro = true, displayName = USER_NAME_ALICE), ) } 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 16021df3e9..14c83db833 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 @@ -44,6 +44,7 @@ import io.element.android.features.roommembermoderation.api.RoomMemberModeration import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState @@ -94,8 +95,8 @@ open class MessagesStateProvider : PreviewParameterProvider { } fun aMessagesState( - roomName: String? = "Room name", - roomAvatar: AvatarData = AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom), + roomName: String? = ROOM_NAME, + roomAvatar: AvatarData = AvatarData("!id:domain", ROOM_NAME, size = AvatarSize.TimelineRoom), userEventPermissions: UserEventPermissions = aUserEventPermissions(), composerState: MessageComposerState = aMessageComposerState( textEditorState = aTextEditorStateRich(initialText = "Hello", initialFocus = true), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt index 47d1947766..be53de5f66 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt @@ -11,6 +11,7 @@ package io.element.android.features.messages.impl.crypto.identity import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.ui.room.IdentityRoomMember @@ -32,7 +33,7 @@ class IdentityChangeStateProvider : PreviewParameterProvider { override val values: Sequence @@ -37,10 +38,10 @@ fun aResolveVerifiedUserSendFailureState( eventSink = eventSink ) -fun anUnsignedDeviceSendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.UnsignedDevice.FromOther( +fun anUnsignedDeviceSendFailure(userDisplayName: String = USER_NAME_ALICE) = VerifiedUserSendFailure.UnsignedDevice.FromOther( userDisplayName = userDisplayName, ) -fun aChangedIdentitySendFailure(userDisplayName: String = "Alice") = VerifiedUserSendFailure.ChangedIdentity( +fun aChangedIdentitySendFailure(userDisplayName: String = USER_NAME_ALICE) = VerifiedUserSendFailure.ChangedIdentity( userDisplayName = userDisplayName, ) 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 9840ac5107..e77fcdb08e 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 @@ -29,6 +29,8 @@ 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.designsystem.preview.ROOM_NAME +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId @@ -143,7 +145,7 @@ internal fun aTimelineItemEvent( isMine: Boolean = false, isEditable: Boolean = false, canBeRepliedTo: Boolean = false, - senderDisplayName: String = "Sender", + senderDisplayName: String = USER_NAME_ALICE, displayNameAmbiguous: Boolean = false, content: TimelineItemEventContent = aTimelineItemTextContent(), groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, @@ -253,7 +255,7 @@ internal fun aGroupedEvents( } internal fun aTimelineRoomInfo( - name: String = "Room name", + name: String = ROOM_NAME, isDm: Boolean = false, userHasPermissionToSendMessage: Boolean = true, pinnedEventIds: List = emptyList(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt index 0506026b86..e298b3af26 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/TypingNotificationStateProvider.kt @@ -9,6 +9,11 @@ package io.element.android.features.messages.impl.typing import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE +import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID +import io.element.android.libraries.designsystem.preview.USER_NAME_EVE import kotlinx.collections.immutable.toImmutableList class TypingNotificationStateProvider : PreviewParameterProvider { @@ -22,7 +27,7 @@ class TypingNotificationStateProvider : PreviewParameterProvider = listOf( - aMatrixUser(id = "@alice:server.org", displayName = "Alice"), - aMatrixUser(id = "@bob:server.org", displayName = "Bob"), - aMatrixUser(id = "@carol:server.org", displayName = "Carol"), + aMatrixUser(displayName = USER_NAME_ALICE), + aMatrixUser(displayName = USER_NAME_BOB), + aMatrixUser(displayName = USER_NAME_CAROL), ), ) = aChangeRolesState( role = role, @@ -114,22 +118,22 @@ internal fun aChangeRolesStateWithOwners( members = persistentListOf( aRoomMember( userId = UserId("@alice:server.org"), - displayName = "Alice", + displayName = USER_NAME_ALICE, role = RoomMember.Role.Owner(isCreator = true), ), aRoomMember( userId = UserId("@bob:server.org"), - displayName = "Bob", + displayName = USER_NAME_BOB, role = RoomMember.Role.Owner(isCreator = false), ), aRoomMember( userId = UserId("@carol:server.org"), - displayName = "Carol", + displayName = USER_NAME_CAROL, role = RoomMember.Role.Admin, ), aRoomMember( userId = UserId("@david:server.org"), - displayName = "David", + displayName = USER_NAME_DAVID, role = RoomMember.Role.User, ), ), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt index bc077feb6a..fb213ef0cc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt @@ -15,6 +15,15 @@ import io.element.android.features.roommembermoderation.api.RoomMemberModeration import io.element.android.features.roommembermoderation.api.RoomMemberModerationState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.map +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CAROL +import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID +import io.element.android.libraries.designsystem.preview.USER_NAME_EVE +import io.element.android.libraries.designsystem.preview.USER_NAME_MALLORY +import io.element.android.libraries.designsystem.preview.USER_NAME_SUSIE +import io.element.android.libraries.designsystem.preview.USER_NAME_VICTOR +import io.element.android.libraries.designsystem.preview.USER_NAME_WALTER import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.RoomMember @@ -143,21 +152,21 @@ fun aRoomMemberList() = persistentListOf( aBannedMallory(), ) -fun anEve(): RoomMember = aRoomMember(UserId("@eve:server.org"), "Eve") +fun anEve(): RoomMember = aRoomMember(UserId("@eve:server.org"), USER_NAME_EVE) -fun aDavid(): RoomMember = aRoomMember(UserId("@david:server.org"), "David") +fun aDavid(): RoomMember = aRoomMember(UserId("@david:server.org"), USER_NAME_DAVID) -fun aCarol(): RoomMember = aRoomMember(UserId("@carol:server.org"), "Carol") +fun aCarol(): RoomMember = aRoomMember(UserId("@carol:server.org"), USER_NAME_CAROL) -fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.Admin) -fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.Moderator) +fun anAlice() = aRoomMember(UserId("@alice:server.org"), USER_NAME_ALICE, role = RoomMember.Role.Admin) +fun aBob() = aRoomMember(UserId("@bob:server.org"), USER_NAME_BOB, role = RoomMember.Role.Moderator) -fun anInvitedVictor() = aRoomMember(UserId("@victor:server.org"), "Victor", membership = RoomMembershipState.INVITE) +fun anInvitedVictor() = aRoomMember(UserId("@victor:server.org"), USER_NAME_VICTOR, membership = RoomMembershipState.INVITE) -fun anInvitedWalter() = aRoomMember(UserId("@walter:server.org"), "Walter", membership = RoomMembershipState.INVITE) +fun anInvitedWalter() = aRoomMember(UserId("@walter:server.org"), USER_NAME_WALTER, membership = RoomMembershipState.INVITE) -fun aBannedSusie(): RoomMember = aRoomMember(UserId("@susie:server.org"), "Susie", membership = RoomMembershipState.BAN) +fun aBannedSusie(): RoomMember = aRoomMember(UserId("@susie:server.org"), USER_NAME_SUSIE, membership = RoomMembershipState.BAN) -fun aBannedMallory(): RoomMember = aRoomMember(UserId("@mallory:server.org"), "Mallory", membership = RoomMembershipState.BAN) +fun aBannedMallory(): RoomMember = aRoomMember(UserId("@mallory:server.org"), USER_NAME_MALLORY, membership = RoomMembershipState.BAN) private fun RoomMember.withIdentity(identityState: IdentityState? = null) = RoomMemberWithIdentityState(this, identityState) diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt index 120a299a7d..2bb4db0c69 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/InternalRoomMemberModerationStateProvider.kt @@ -14,6 +14,7 @@ import io.element.android.features.roommembermoderation.api.ModerationActionStat import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.toImmutableList @@ -78,8 +79,8 @@ class InternalRoomMemberModerationStateProvider : PreviewParameterProvider { } fun aLeaveSpaceState( - spaceName: String? = "Space name", + spaceName: String? = SPACE_NAME, isLastOwner: Boolean = false, areCreatorsPrivileged: Boolean = false, selectableSpaceRooms: AsyncData> = AsyncData.Uninitialized, diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt index 2030b6885a..36f6a2a1d0 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/settings/SpaceSettingsStateProvider.kt @@ -9,6 +9,7 @@ package io.element.android.features.space.impl.settings import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.SPACE_NAME import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId @@ -24,7 +25,7 @@ open class SpaceSettingsStateProvider : PreviewParameterProvider { open class MatrixUserWithAvatarProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aMatrixUser(displayName = "John Doe"), - aMatrixUser(displayName = "John Doe", avatarUrl = "anUrl"), + aMatrixUser(displayName = USER_NAME_JOHN_DOE), + aMatrixUser(displayName = USER_NAME_JOHN_DOE, avatarUrl = "anUrl"), ) } fun aMatrixUser( - id: String = "@id_of_alice:server.org", - displayName: String? = "Alice", + id: String? = null, + displayName: String? = USER_NAME_ALICE, avatarUrl: String? = null, ) = MatrixUser( - userId = UserId(id), + userId = UserId(id ?: "@${displayName?.lowercase() ?: "id_of_alice"}:server.org"), displayName = displayName, avatarUrl = avatarUrl, ) fun aMatrixUserList() = listOf( - aMatrixUser("@alice:server.org", "Alice"), - aMatrixUser("@bob:server.org", "Bob"), - aMatrixUser("@carol:server.org", "Carol"), - aMatrixUser("@david:server.org", "David"), - aMatrixUser("@eve:server.org", "Eve"), - aMatrixUser("@justin:server.org", "Justin"), - aMatrixUser("@mallory:server.org", "Mallory"), - aMatrixUser("@susie:server.org", "Susie"), - aMatrixUser("@victor:server.org", "Victor"), - aMatrixUser("@walter:server.org", "Walter"), + aMatrixUser(displayName = USER_NAME_ALICE), + aMatrixUser(displayName = USER_NAME_BOB), + aMatrixUser(displayName = USER_NAME_CAROL), + aMatrixUser(displayName = USER_NAME_DAVID), + aMatrixUser(displayName = USER_NAME_EVE), + aMatrixUser(displayName = USER_NAME_JUSTIN), + aMatrixUser(displayName = USER_NAME_MALLORY), + aMatrixUser(displayName = USER_NAME_SUSIE), + aMatrixUser(displayName = USER_NAME_VICTOR), + aMatrixUser(displayName = USER_NAME_WALTER), ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt index b118dbcbe3..17ec7c6ec7 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUser.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_JOHN_DOE import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName @@ -58,7 +59,7 @@ internal fun SelectedUserRtlPreview() = CompositionLocalProvider( ) { ElementPreview { SelectedUser( - matrixUser = aMatrixUser(displayName = "John Doe"), + matrixUser = aMatrixUser(displayName = USER_NAME_JOHN_DOE), canRemove = true, onUserRemove = {}, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt index c3e8292147..97aa6ea613 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt @@ -30,6 +30,10 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE +import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.spaces.SpaceRoomVisibility import io.element.android.libraries.matrix.api.user.MatrixUser @@ -120,10 +124,10 @@ internal fun SpaceHeaderViewPreview() = ElementPreview { topicMaxLines = 2, visibility = SpaceRoomVisibility.Public, heroes = persistentListOf( - aMatrixUser(id = "@1:d", displayName = "Alice", avatarUrl = "aUrl"), - aMatrixUser(id = "@2:d", displayName = "Bob"), - aMatrixUser(id = "@3:d", displayName = "Charlie", avatarUrl = "aUrl"), - aMatrixUser(id = "@4:d", displayName = "Dave"), + aMatrixUser(id = "@1:d", displayName = USER_NAME_ALICE, avatarUrl = "aUrl"), + aMatrixUser(id = "@2:d", displayName = USER_NAME_BOB), + aMatrixUser(id = "@3:d", displayName = USER_NAME_CHARLIE, avatarUrl = "aUrl"), + aMatrixUser(id = "@4:d", displayName = USER_NAME_DAVID), ), numberOfMembers = 999, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceMembersView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceMembersView.kt index 202ea79d87..6d927989d5 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceMembersView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceMembersView.kt @@ -22,6 +22,10 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE +import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.user.MatrixUser @@ -98,10 +102,10 @@ internal fun SpaceMembersViewPreview() = ElementPreview( ) { SpaceMembersView( heroes = persistentListOf( - aMatrixUser(id = "@1:d", displayName = "Alice", avatarUrl = "aUrl"), - aMatrixUser(id = "@2:d", displayName = "Bob"), - aMatrixUser(id = "@3:d", displayName = "Charlie", avatarUrl = "aUrl"), - aMatrixUser(id = "@4:d", displayName = "Dave"), + aMatrixUser(id = "@1:d", displayName = USER_NAME_ALICE, avatarUrl = "aUrl"), + aMatrixUser(id = "@2:d", displayName = USER_NAME_BOB), + aMatrixUser(id = "@3:d", displayName = USER_NAME_CHARLIE, avatarUrl = "aUrl"), + aMatrixUser(id = "@4:d", displayName = USER_NAME_DAVID), ), numberOfMembers = 123, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt index db63bba779..d6a582d83c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceRoomProvider.kt @@ -9,6 +9,8 @@ package io.element.android.libraries.matrix.ui.components import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.SPACE_NAME +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomType @@ -28,10 +30,10 @@ class SpaceRoomProvider : PreviewParameterProvider { state = CurrentUserMembership.LEFT, ), aSpaceRoom( - displayName = "Alice", + displayName = SPACE_NAME, roomType = RoomType.Room, isDirect = true, - heroes = listOf(aMatrixUser(displayName = "Alice")), + heroes = listOf(aMatrixUser(displayName = USER_NAME_ALICE)), state = CurrentUserMembership.JOINED, numJoinedMembers = 2, ), @@ -69,9 +71,9 @@ class SpaceRoomProvider : PreviewParameterProvider { state = CurrentUserMembership.INVITED, ), aSpaceRoom( - displayName = "Alice", + displayName = SPACE_NAME, roomType = RoomType.Space, - heroes = listOf(aMatrixUser(displayName = "Alice")), + heroes = listOf(aMatrixUser(displayName = USER_NAME_ALICE)), state = CurrentUserMembership.JOINED, numJoinedMembers = 2, ), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt index 5ddf57b723..b66beb009c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.ui.messages.reply import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE 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.MediaSource @@ -159,7 +160,7 @@ private fun aInReplyToDetails( ) fun aProfileDetailsReady( - displayName: String? = "Sender", + displayName: String? = USER_NAME_ALICE, displayNameAmbiguous: Boolean = false, avatarUrl: String? = null, ) = ProfileDetails.Ready( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt index d5fd46d507..72b16c4e1d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDeleteConfirmationProvider.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.details import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.mediaviewer.api.MediaInfo @@ -26,7 +27,7 @@ open class MediaBottomSheetStateDeleteConfirmationProvider : PreviewParameterPro fun aMediaBottomSheetStateDeleteConfirmation( mediaInfo: MediaInfo = anImageMediaInfo( - senderName = "Alice", + senderName = USER_NAME_ALICE, ), thumbnailSource: MediaSource? = null, ) = MediaBottomSheetState.DeleteConfirmation( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt index e79bfa5e77..99a6925b9d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.mediaviewer.impl.details import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.anApkMediaInfo @@ -35,7 +36,7 @@ fun aMediaBottomSheetStateDetails( eventId: EventId? = EventId($$"$eventId"), canDelete: Boolean = true, mediaInfo: MediaInfo = anImageMediaInfo( - senderName = "Alice", + senderName = USER_NAME_ALICE, dateSentFull = "December 6, 2024 at 12:59", ), ) = MediaBottomSheetState.Details( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt index 7b5edf594a..75b849cd0c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryStateProvider.kt @@ -11,6 +11,7 @@ package io.element.android.libraries.mediaviewer.impl.gallery import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.media.WaveFormSamples +import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.impl.details.aMediaBottomSheetStateDetails @@ -112,7 +113,7 @@ open class MediaGalleryStateProvider : PreviewParameterProvider = AsyncData.Uninitialized, mediaBottomSheetState: MediaBottomSheetState = MediaBottomSheetState.Hidden, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index ef9b533586..cb1da7b9b2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.media.WaveFormSamples +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.Timeline @@ -179,7 +180,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider ) ), anImageMediaInfo( - senderName = "Alice", + senderName = USER_NAME_ALICE, dateSent = "21 NOV, 2024", caption = LONG_CAPTION, ).let { From a77c2c0c7e49203c22a6045361d65b46a1965982 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 17:54:22 +0200 Subject: [PATCH 097/471] Update ref to submodule. --- enterprise | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enterprise b/enterprise index 1ed9a7e8b4..fb7e9287d9 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 1ed9a7e8b4406a5e6d12f603cfc2083e84f8576f +Subproject commit fb7e9287d9d446012925139842d9aaa8e99a74dc From 223dd1f6b0523173e0ba7003a91bee8e4d8b6991 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 18:27:49 +0200 Subject: [PATCH 098/471] Fix invalid userId. --- .../messages/impl/timeline/TimelineStateProvider.kt | 6 +++--- .../android/libraries/designsystem/preview/PreviewData.kt | 1 + .../libraries/matrix/ui/components/MatrixUserProvider.kt | 2 +- .../matrix/ui/messages/reply/InReplyToDetailsProvider.kt | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) 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 e77fcdb08e..0bf293eca4 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 @@ -30,7 +30,7 @@ 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.designsystem.preview.ROOM_NAME -import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_SENDER import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId @@ -145,7 +145,7 @@ internal fun aTimelineItemEvent( isMine: Boolean = false, isEditable: Boolean = false, canBeRepliedTo: Boolean = false, - senderDisplayName: String = USER_NAME_ALICE, + senderDisplayName: String = USER_NAME_SENDER, displayNameAmbiguous: Boolean = false, content: TimelineItemEventContent = aTimelineItemTextContent(), groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, @@ -162,7 +162,7 @@ internal fun aTimelineItemEvent( eventId = eventId, transactionId = transactionId, senderId = UserId("@senderId:domain"), - senderAvatar = AvatarData("@senderId:domain", "sender", size = AvatarSize.TimelineSender), + senderAvatar = AvatarData("@senderId:domain", USER_NAME_SENDER, size = AvatarSize.TimelineSender), content = content, reactionsState = timelineItemReactions, readReceiptState = readReceiptState, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewData.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewData.kt index f728cce302..66a6043634 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewData.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewData.kt @@ -16,6 +16,7 @@ const val USER_NAME_EVE = "Eve" const val USER_NAME_JOHN_DOE = "John Doe" const val USER_NAME_JUSTIN = "Justin" const val USER_NAME_MALLORY = "Mallory" +const val USER_NAME_SENDER = "Sender" const val USER_NAME_SUSIE = "Susie" const val USER_NAME_VICTOR = "Victor" const val USER_NAME_WALTER = "Walter" diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt index 84a226edc7..1b662ad7c1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt @@ -44,7 +44,7 @@ fun aMatrixUser( displayName: String? = USER_NAME_ALICE, avatarUrl: String? = null, ) = MatrixUser( - userId = UserId(id ?: "@${displayName?.lowercase() ?: "id_of_alice"}:server.org"), + userId = UserId(id ?: "@${displayName?.lowercase()?.replace(" ", "_") ?: "id_of_alice"}:server.org"), displayName = displayName, avatarUrl = avatarUrl, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt index b66beb009c..ca118349e9 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/messages/reply/InReplyToDetailsProvider.kt @@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.ui.messages.reply import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_SENDER 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.MediaSource @@ -160,7 +160,7 @@ private fun aInReplyToDetails( ) fun aProfileDetailsReady( - displayName: String? = USER_NAME_ALICE, + displayName: String? = USER_NAME_SENDER, displayNameAmbiguous: Boolean = false, avatarUrl: String? = null, ) = ProfileDetails.Ready( From a7c4a2b4044a73d86989ff8c484e9fed5f0fef32 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 6 May 2026 20:17:22 +0000 Subject: [PATCH 099/471] Update screenshots --- ...te.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en.png | 4 ++-- ...te.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en.png | 4 ++-- ....impl.acceptdecline_AcceptDeclineInviteView_Night_1_en.png | 4 ++-- ....impl.acceptdecline_AcceptDeclineInviteView_Night_2_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_0_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_2_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_3_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_4_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_5_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_0_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_2_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_3_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_4_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_5_en.png | 4 ++-- ...eatures.preferences.impl.user_UserPreferences_Day_0_en.png | 4 ++-- ...tures.preferences.impl.user_UserPreferences_Night_0_en.png | 4 ++-- ...tchat.impl.components_SearchMultipleUsersResultItem_en.png | 4 ++-- ...tartchat.impl.components_SearchSingleUserResultItem_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Day_1_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Day_2_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Day_4_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Night_1_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Night_2_en.png | 4 ++-- .../features.startchat.impl.root_StartChatView_Night_4_en.png | 4 ++-- .../features.userprofile.shared_UserProfileView_Day_8_en.png | 4 ++-- ...features.userprofile.shared_UserProfileView_Night_8_en.png | 4 ++-- ...aries.matrix.ui.components_CheckableResolvedUserRow_en.png | 4 ++-- ...ies.matrix.ui.components_CheckableUnresolvedUserRow_en.png | 4 ++-- ...ui.components_CreateDmConfirmationBottomSheet_Day_0_en.png | 4 ++-- ...ui.components_CreateDmConfirmationBottomSheet_Day_1_en.png | 4 ++-- ....components_CreateDmConfirmationBottomSheet_Night_0_en.png | 4 ++-- ....components_CreateDmConfirmationBottomSheet_Night_1_en.png | 4 ++-- ...braries.matrix.ui.components_MatrixUserHeader_Day_0_en.png | 4 ++-- ...aries.matrix.ui.components_MatrixUserHeader_Night_0_en.png | 4 ++-- .../libraries.matrix.ui.components_MatrixUserRow_Day_0_en.png | 4 ++-- ...ibraries.matrix.ui.components_MatrixUserRow_Night_0_en.png | 4 ++-- ...matrix.ui.components_SelectedUserCannotRemove_Day_0_en.png | 4 ++-- ...trix.ui.components_SelectedUserCannotRemove_Night_0_en.png | 4 ++-- ...ibraries.matrix.ui.components_SelectedUserRtl_Day_0_en.png | 4 ++-- ...raries.matrix.ui.components_SelectedUserRtl_Night_0_en.png | 4 ++-- .../libraries.matrix.ui.components_SelectedUser_Day_0_en.png | 4 ++-- ...libraries.matrix.ui.components_SelectedUser_Night_0_en.png | 4 ++-- ...raries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png | 4 ++-- ...raries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png | 4 ++-- ...ries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png | 4 ++-- ...ries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png | 4 ++-- .../libraries.matrix.ui.components_UnresolvedUserRow_en.png | 4 ++-- 47 files changed, 94 insertions(+), 94 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en.png index bc93c4c316..a0ab0a2192 100644 --- a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95096ef9f55d57ea4774f8eaa91d92a01fd44eafd4670442d3db132ff843ede0 -size 21476 +oid sha256:62fee8e8966ff4df0d951aff1d13e8578c9c5a55e89c4407dcc8077c9b1b72af +size 22249 diff --git a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en.png index d1c7598599..cce585fe3f 100644 --- a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b53f36378be4b4da2608210cb3134923c6f4bf565652c80a1b45cad8216d67da -size 25485 +oid sha256:d3226560d55a9fde8175206c0ce21cb4cd90d900d357003496bf74b719281166 +size 26191 diff --git a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en.png index 54a3c425c1..6a278db2b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d549ca3000080a6d0c416d7a45d530da96c73184309048126be6304393bc74f9 -size 20082 +oid sha256:643f712799406118deb87af3fab34e8838878dc04e0935b4e54dafaaebd03479 +size 20895 diff --git a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en.png index 341bcca1a6..2b09d75bc9 100644 --- a/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a61181eab30d30f56c5407bb42395a268ff80f7dd455ea04a468f92739e0a87f -size 24194 +oid sha256:c2638b57f0f4ab1876429f096065d121122eaca0b9b06600281526dbf51d7791 +size 24843 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 c98615fb74..9d8eec80cb 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:1da26fc2ffe2216cda7aa1b6b92ba587c9d0bb56aa54586b3073befee96d29b6 -size 40956 +oid sha256:e83c8d052ce340010f97c4ef2750e4b921a129639a0004bd951f71c7348622bf +size 40597 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_2_en.png index 9f15832109..18869e3590 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a4be2c70eb477dbd58e4e6924d0141f1c8d7205807d968b3a34221c12a301c6 -size 36176 +oid sha256:95e30b24d5a6571cf42a707a01f0458cf509428c9ab8333924e1679f6d4d4f31 +size 35778 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_3_en.png index f1b39a0d35..2786b0cb8d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9176ee2b5b78032639d9f51f7680d82f7d3ca916fb587d914f12d075382d65f0 -size 27077 +oid sha256:8e54c2c665235d6b4c45161b4fcc0b8f0a96ce7620bbed9f56e04ecb71916f3e +size 26640 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_4_en.png index e9cb1772f7..ff630d5aab 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76caf93913c979f3327dec4331d3a5bea5027df45c5cacf55a786b8f677e3162 -size 27340 +oid sha256:c2fd721612e0620c3a4eac0f0835ac1d03ffb36ec45477340bb732ff1c63cc30 +size 26921 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_5_en.png index 31a44423ce..cb6c51d959 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e457b722fe403205f1394e7347dedb3aba308e48c979ea002399425c9a130fd2 -size 20667 +oid sha256:7c7fc020ec4bcb2f67a3a534a621e71233488b469b24e6bd6d3efa42f9f944a1 +size 20263 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 ebe4511bdf..5fe3875d72 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:6a0202d081e5aed85ca093239fae1c8f46d42fd6e859c818f5c0301597e5b473 -size 41957 +oid sha256:a07bd9ac57c1fb6074b81134edcd169c2c7edc3dd19039766dabbfe8ccd5d8fb +size 41621 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_2_en.png index 155302aafc..c188be2848 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ae20b874dba38a89a59e418bce33e748449446e0fc913b7f3ff2e87b8200c40 -size 36568 +oid sha256:67bc6eac2128230323aa7dabb6896b98c4249e6fba9edb4d1769f36dbe031135 +size 36346 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_3_en.png index 15c12f4836..f2ca7b4a6c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74a4913734d0648115d5056052fa2de8a839bb5a4d2dfdaa3d8ed5f0eef2793d -size 27728 +oid sha256:a40fe5dc4a671ec2384265da36f3d42a3cadb2792823308a62dbd82423110652 +size 27552 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_4_en.png index 7e4a6475b1..8ce81e889d 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4f8571893bf9292a7d298c7419ea2e3f0363e1c2eca8c5f6aaea7d39143039c -size 27855 +oid sha256:007aeb20270db25edcd3cf2e1ac8e4e0d714e08a043af0473c2263c1e79af452 +size 27634 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_5_en.png index c325a4e7b5..c402bf81c9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.root_PreferencesRootViewLight_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d5b500d6275bc6ead5e8644b1afb1a0a829e91d4912444e5e0d431322343855 -size 20700 +oid sha256:d644bc2a62596b923d782150334f60b1940730824af0b7a304d2fdfce85cd0e6 +size 20493 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_0_en.png index c6e079dee2..03d5ad5880 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0805bac4bf9e1c5bb16b4f81b004bcc952563eef98101d8c9c6e856a414977d0 -size 11219 +oid sha256:0dfea70e781debf7a293aee2967c3da314101020698fee97d3551e25f5f378d0 +size 10655 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_0_en.png index f21d99fffc..27f51b2778 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:070163694fe10b465f2903ce2cc88f9ffc67d9489aa5a4e204cd087fd02b8642 -size 11281 +oid sha256:62e127900d00756ecc806eb9d8efb6a11cd9cdcecae6b4bab643b7306a06be2b +size 10435 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchMultipleUsersResultItem_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchMultipleUsersResultItem_en.png index cad7e55b37..de213ca597 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchMultipleUsersResultItem_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchMultipleUsersResultItem_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8af941ad8e5b553b82aad8b39abd8b4876d67b6906acf0ee8e4715673d8b35a -size 81462 +oid sha256:80e8c8bf0bf9b34d3de0b0db7f67b84a667a9f510d6e9eefc708de25dd1ca5f8 +size 76725 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchSingleUserResultItem_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchSingleUserResultItem_en.png index b5e9457b1a..a980e0c914 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchSingleUserResultItem_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.components_SearchSingleUserResultItem_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f776cecd5278761fe7904b63dd980804a6dc2d3e7acc37fb7ea71d6a1c1e63df -size 42505 +oid sha256:d7e8bc45d019ed73b4795f29b36c28411e8e20cd4bd231e76e8619e011b64b31 +size 40203 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_1_en.png index a2f44ea5a0..33fa7369ed 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b094f8d20b4b946abcd95bbb0c1d64a0190a5c651b6f434dbd9aef2857a315a7 -size 19991 +oid sha256:27249bb1a928cb325557c8aebf6de344b7cd7a16d3420e8711591f20a3eb36b1 +size 18947 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_2_en.png index 5e3737d938..ac26997f26 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3fde1e89269c46ac8a6b4f0a05aad45bcc806f2a1b6108c09f7078209f7799c -size 26574 +oid sha256:a5f0abeb2bfbae8fc6b864ea2e6a90b4ab3bcfd3b9f5fa4a06c6a314bd194a5f +size 25660 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png index c2b61f4018..51f05258df 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:657e469b19b815ad54ef7ccd6a140d33031301de504690cb520c482f469fa8c4 -size 38559 +oid sha256:4b372f9fd8570c977cd882f396004fe8fd0794ca3070a11394e0d6af6354987a +size 38154 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_1_en.png index 5e457ea444..b37d6fa5d9 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f37ae64424323d6772fca7b43f175e8c02356833bed02bb9661acbfb102c194 -size 19358 +oid sha256:c5e6a562525d8ded0fddb365beaef933d2701bcd4548e589bffd54e1bd0a3b11 +size 18087 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_2_en.png index 6fe0a0be87..4592dbb299 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d42e8795792b250696206f5f9fabcac3b7249b3709cf90d8f2503091769e5343 -size 25324 +oid sha256:e22f1f07193011c1db785e3827e73d05d8bf96fd12559e3d7a744b2b978b7bf4 +size 24196 diff --git a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png index cc73f71d0c..76810b5897 100644 --- a/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.startchat.impl.root_StartChatView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02a03b83eaf866a7a65ec26f57fa810a08ad7128d11d2db6ad203bf405a1bb05 -size 37097 +oid sha256:a8a1a8fe51f741748817a0a1c497fc164d0e1e711ce458182a654b1fee8e5741 +size 36417 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_8_en.png index d0d02d6321..aef669fae0 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65446447e07a1a6cc08ed566e1aaea7a1209f1e1f5f752dec5e598819333761d -size 34595 +oid sha256:781bf8c86e88c9ee564f45de7446ee2eb554efe68ae9e708aa1b5bde7fce6d6b +size 34137 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_8_en.png index 844a7c3c54..3c90a4f7ef 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e467732ff44f080bb1a698d4f6f2427480c5456980066ffd9ab6970b093c3d64 -size 33206 +oid sha256:45c706bb63ab7df8e66898ec67fabbf8c227cac421b8c5eda84a2f6532c8f199 +size 32504 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableResolvedUserRow_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableResolvedUserRow_en.png index 3aa2f3157d..28e63caa03 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableResolvedUserRow_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableResolvedUserRow_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a986526c80130e11a0ec9c6450e4cd77dc40ef4a09082a1c526eebabebf4c03 -size 49746 +oid sha256:d372726c65f1887a8eba63f04301212780ef04410fd0758d314562f6899859ea +size 46072 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableUnresolvedUserRow_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableUnresolvedUserRow_en.png index 1ef0d7dd02..1cd0c138e2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableUnresolvedUserRow_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CheckableUnresolvedUserRow_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2ca690a89bebaa351990cab34155afcfa7a0b9f02312757fcb933ba97ab9310 -size 101952 +oid sha256:ea6ea46123a211b87daadf29aa87588f4e0fa5e5c25ddc0307db490c72f865d5 +size 96925 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en.png index c7e3599c58..1c57c30014 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb4d6bfb9c412de00a2b4956032dd42906b5451eb99e6ebb1880dc01f6b55af5 -size 26077 +oid sha256:fbbe069eaeff3c1bfb39e4b3e3356e92864f10a57ca4ccb2029e38b5977b6f45 +size 25648 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en.png index d7ff4a1d2f..e8d101b4b7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26bf76ccdb56d042422553f557d91d0f26d874a710f696ac106c5c2b5590d332 -size 38833 +oid sha256:97e6bbc6cc6d5bb90606bc7f934258c8060d7ca5fade8da6a1aae288e8558bb4 +size 38390 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en.png index fe44b8941c..0a5082edbe 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8c422787b67d477d3b7c8d5dee8879f33d47153dc93dd29bb3883e4ed863a41 -size 25232 +oid sha256:2b14f8a5a50deb5dd4854665cd41511228f5ba47b0719bf35b4d1675d0f20ce1 +size 24527 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en.png index f5ff7856b2..6d872bf044 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8413aed02383572cfe8c481c6ba8b0db4cfb3402334c37f2d8b54a73fe4bf594 -size 37343 +oid sha256:841042eabcd85626a5182108edae46b4514f84a8f22c57e324c02430536acff2 +size 36738 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_0_en.png index c6e079dee2..03d5ad5880 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0805bac4bf9e1c5bb16b4f81b004bcc952563eef98101d8c9c6e856a414977d0 -size 11219 +oid sha256:0dfea70e781debf7a293aee2967c3da314101020698fee97d3551e25f5f378d0 +size 10655 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_0_en.png index f21d99fffc..27f51b2778 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:070163694fe10b465f2903ce2cc88f9ffc67d9489aa5a4e204cd087fd02b8642 -size 11281 +oid sha256:62e127900d00756ecc806eb9d8efb6a11cd9cdcecae6b4bab643b7306a06be2b +size 10435 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_0_en.png index 78e3e7db33..93e17f26b8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9aa5b37b57d1ef1f219e0c5213eab3eba3222bd0453988a5c708e15ba89d8fd4 -size 9772 +oid sha256:4c37d48eb1fde1eb67cb3130505414c97486d1b40b49be9a6c0770b0ff9e50da +size 9277 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_0_en.png index af7347092d..d55e71d415 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e850f65631de21c8cb44965f66e8ffbf0b1eecf2ed6f91e6669f0dc89ec0e4b3 -size 9784 +oid sha256:4e283d48e8eb569f44576c158096ef0672146392411e2e768350ebaff9f71ee7 +size 9073 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en.png index 13b54d61f4..6fc398e5ba 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca4b2ddcebb957944522673d20aafb967ef118ac1a8564f33a0c8745f0f7caf5 -size 6268 +oid sha256:2a6797d1d45c1ef189dfffec805604b06dd7820976c834233a99fc803721b663 +size 6500 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en.png index 30475df2c4..b599a59788 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d56cc64425872a2dc1c12ab7d2e0d9fe43e89586e6f5f1805684350281baa6b8 -size 6744 +oid sha256:acc4553e995907ecfaaf74c4e7bfdd9fd8ccf5cd6ba8396b25c110211c21975e +size 6614 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Day_0_en.png index 81b127dc0b..1d334393b1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:160abf1659329b6ddb61e9f3bbe9cc5cf4630242429093a8d2d579e0b6efd78d -size 7687 +oid sha256:2f93df7decca6ced4efe9f739d6f877b2411be874c0cf011a146a66e0ee2fb69 +size 7790 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Night_0_en.png index 7ad44c9fd2..efe44c5f81 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUserRtl_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39817cc9c0bcb7ae058201c8bb9e7f68529142161019a23296087cd952667473 -size 8143 +oid sha256:7a52a67692a0fefcb499af2f3e0670b663b1df657ecc7b61e58773db9ea6615f +size 8057 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Day_0_en.png index 08ebefd43b..230de5c2ef 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40efbe8c661131800679fcdf18870d05cbd24a7f62c7c1adb998f28cef3896ea -size 7698 +oid sha256:b095f97e5ac7279fba15f7ea8dc1658c89e28e4f72b8f93b7c32a7b6d5bc4d23 +size 7807 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Night_0_en.png index b7d94eec19..d45e2d4b35 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SelectedUser_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:774bf98c634fef1339d95e8eaf5f5219535766f41f2ee8b4c124107f381bf052 -size 8167 +oid sha256:5a0960b2353e902b04682818cf598fad7ff4ccee9e9fe7aec13ad556653b702c +size 8091 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png index 25dae1f86b..82f7e36af6 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ad5934da41af5f5f350adc3d5d754f1ed16e90546cb20a0874e3d069010167d -size 10117 +oid sha256:aa985c9b0211cb12e617e8da73669777cf3a5ec5b77b79f232ccdc6e0cf55373 +size 11760 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png index 491523e822..e38dfee86a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81de93c5c71bc1552dc93343bae9168ce35e0d4d4430a47311ff80b9cec6c3b6 -size 10633 +oid sha256:71bf90e818949065c7620bfa8e7148a8f9be295775e1b29cb251d2a00bd87853 +size 12173 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png index 7201e63c00..e3c16b4b40 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ec4038e9d1cd25d8ee27c54e10f9f3627bf6f8f4c83201b1bbc3fd73641f1d5 -size 10333 +oid sha256:68e990772361fd240fb20b664731613b5a2f187499f2db48e130673f16acb33b +size 11575 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png index 308066382b..f4bcc684a7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2a837de06ec9369c5a39629579c237b4f394a586a2ce85a5c5e15d444c98314 -size 10323 +oid sha256:05e2b62d4484990cdd837ff233a654cd6aa8d82d1c3965351bba86bcfcc1e1d0 +size 11776 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_UnresolvedUserRow_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_UnresolvedUserRow_en.png index 1e4ce1ae37..5d59b52717 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_UnresolvedUserRow_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_UnresolvedUserRow_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86d4aaa9a2ad499be90ed3a0d9a15fcd23cba4b40395aee53028168717c1fe9f -size 54739 +oid sha256:8a2d52c8f8903881357cd0590d5c1bfb85997068eded3813aad1bec4f31d853a +size 52452 From a27278da2075950801ecbc6c088aaac7d2967da7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 08:09:15 +0200 Subject: [PATCH 100/471] Update dependencyAnalysis to v3.10.0 (#6742) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c03d164728..581630c80d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,7 +51,7 @@ telephoto = "0.19.0" haze = "1.7.2" # Dependency analysis -dependencyAnalysis = "3.9.0" +dependencyAnalysis = "3.10.0" # DI metro = "0.13.2" From 034916269483e875e9c8e564804e3fb40d029e7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 May 2026 09:50:00 +0200 Subject: [PATCH 101/471] More replacements --- .../suggestions/SuggestionsPickerView.kt | 3 +- .../previewutils/room/RoomMemberFixture.kt | 30 ++++++++++++------- .../previewutils/room/SpaceRoomFixture.kt | 3 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt index 678ef2ba56..3d18db12d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType.Ro import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.RoomAlias @@ -198,7 +199,7 @@ internal fun SuggestionsPickerViewPreview() { suggestions = persistentListOf( ResolvedSuggestion.AtRoom, ResolvedSuggestion.Member(roomMember), - ResolvedSuggestion.Member(roomMember.copy(userId = UserId("@bob:server.org"), displayName = "Bob")), + ResolvedSuggestion.Member(roomMember.copy(userId = UserId("@bob:server.org"), displayName = USER_NAME_BOB)), ResolvedSuggestion.Alias( roomAlias = anAlias, roomId = RoomId("!room:matrix.org"), diff --git a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt index 1b73ccea31..42fc527bb2 100644 --- a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt +++ b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt @@ -8,6 +8,16 @@ package io.element.android.libraries.previewutils.room +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB +import io.element.android.libraries.designsystem.preview.USER_NAME_CAROL +import io.element.android.libraries.designsystem.preview.USER_NAME_DAVID +import io.element.android.libraries.designsystem.preview.USER_NAME_EVE +import io.element.android.libraries.designsystem.preview.USER_NAME_JUSTIN +import io.element.android.libraries.designsystem.preview.USER_NAME_MALLORY +import io.element.android.libraries.designsystem.preview.USER_NAME_SUSIE +import io.element.android.libraries.designsystem.preview.USER_NAME_VICTOR +import io.element.android.libraries.designsystem.preview.USER_NAME_WALTER import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState @@ -38,27 +48,27 @@ fun aRoomMember( fun aRoomMemberList() = persistentListOf( anAlice(), aBob(), - aRoomMember(UserId("@carol:server.org"), "Carol"), - aRoomMember(UserId("@david:server.org"), "David"), - aRoomMember(UserId("@eve:server.org"), "Eve"), - aRoomMember(UserId("@justin:server.org"), "Justin"), - aRoomMember(UserId("@mallory:server.org"), "Mallory"), - aRoomMember(UserId("@susie:server.org"), "Susie"), + aRoomMember(UserId("@carol:server.org"), USER_NAME_CAROL), + aRoomMember(UserId("@david:server.org"), USER_NAME_DAVID), + aRoomMember(UserId("@eve:server.org"), USER_NAME_EVE), + aRoomMember(UserId("@justin:server.org"), USER_NAME_JUSTIN), + aRoomMember(UserId("@mallory:server.org"), USER_NAME_MALLORY), + aRoomMember(UserId("@susie:server.org"), USER_NAME_SUSIE), aVictor(), aWalter(), ) -fun anAlice() = aRoomMember(UserId("@alice:server.org"), "Alice", role = RoomMember.Role.Admin) -fun aBob() = aRoomMember(UserId("@bob:server.org"), "Bob", role = RoomMember.Role.Moderator) +fun anAlice() = aRoomMember(UserId("@alice:server.org"), USER_NAME_ALICE, role = RoomMember.Role.Admin) +fun aBob() = aRoomMember(UserId("@bob:server.org"), USER_NAME_BOB, role = RoomMember.Role.Moderator) fun aVictor() = aRoomMember( UserId("@victor:server.org"), - "Victor", + USER_NAME_VICTOR, membership = RoomMembershipState.INVITE ) fun aWalter() = aRoomMember( UserId("@walter:server.org"), - "Walter", + USER_NAME_WALTER, membership = RoomMembershipState.INVITE ) diff --git a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt index 4bf1d2501b..68825540a4 100644 --- a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt +++ b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.previewutils.room +import io.element.android.libraries.designsystem.preview.SPACE_NAME import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership @@ -19,7 +20,7 @@ import kotlinx.collections.immutable.toImmutableList fun aSpaceRoom( rawName: String? = null, - displayName: String = "Space name", + displayName: String = SPACE_NAME, avatarUrl: String? = null, canonicalAlias: RoomAlias? = null, childrenCount: Int = 0, From b19a2c5a443093462a381a49dfd90ed7ed7f431c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 May 2026 09:50:12 +0200 Subject: [PATCH 102/471] Use generic id for default --- .../libraries/matrix/ui/components/MatrixUserProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt index 1b662ad7c1..639bafbcbf 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserProvider.kt @@ -44,7 +44,7 @@ fun aMatrixUser( displayName: String? = USER_NAME_ALICE, avatarUrl: String? = null, ) = MatrixUser( - userId = UserId(id ?: "@${displayName?.lowercase()?.replace(" ", "_") ?: "id_of_alice"}:server.org"), + userId = UserId(id ?: "@${displayName?.lowercase()?.replace(" ", "_") ?: "id"}:server.org"), displayName = displayName, avatarUrl = avatarUrl, ) From 4948497be29d2555235c0e9d03d7117b2bd94268 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 May 2026 09:53:33 +0200 Subject: [PATCH 103/471] More replacements --- .../features/home/impl/components/HomeTopBar.kt | 11 ++++++----- .../location/impl/common/ui/LocationShareRow.kt | 10 ++++++---- .../messages/impl/threads/list/ThreadsListView.kt | 6 ++++-- .../impl/timeline/components/MessageShieldView.kt | 3 ++- .../impl/timeline/components/TimelineItemEventRow.kt | 5 +++-- .../components/TimelineItemEventRowUtdPreview.kt | 8 +++++--- .../virtual/TimelineItemRoomBeginningView.kt | 5 +++-- .../messages/impl/topbars/MessagesViewTopBar.kt | 5 +++-- .../features/messages/impl/topbars/ThreadTopBar.kt | 7 ++++--- .../user/editprofile/EditUserProfileStateProvider.kt | 3 ++- .../userprofile/shared/UserProfileHeaderSection.kt | 5 +++-- .../impl/ui/VerificationUserProfileContent.kt | 3 ++- .../libraries/designsystem/components/LocationPin.kt | 3 ++- .../designsystem/components/avatar/DmAvatars.kt | 6 ++++-- .../matrix/ui/components/InviteSenderView.kt | 5 +++-- .../matrix/ui/components/OrganizationHeader.kt | 3 ++- .../libraries/matrix/ui/components/SpaceHeaderView.kt | 3 ++- 17 files changed, 56 insertions(+), 35 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 5c3075fc75..5f55beaf60 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -57,6 +57,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem @@ -65,8 +66,8 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.testtags.TestTags @@ -346,7 +347,7 @@ private fun AccountIcon( internal fun HomeTopBarPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), + currentUserAndNeighbors = persistentListOf(aMatrixUser(id = "@id:domain", displayName = USER_NAME_ALICE)), showAvatarIndicator = false, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), @@ -367,7 +368,7 @@ internal fun HomeTopBarPreview() = ElementPreview { internal fun HomeTopBarSpaceFiltersSelectedPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), + currentUserAndNeighbors = persistentListOf(aMatrixUser(id = "@id:domain", displayName = USER_NAME_ALICE)), showAvatarIndicator = false, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), @@ -388,7 +389,7 @@ internal fun HomeTopBarSpaceFiltersSelectedPreview() = ElementPreview { internal fun HomeTopBarSpacesPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Spaces, - currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), + currentUserAndNeighbors = persistentListOf(aMatrixUser(id = "@id:domain", displayName = USER_NAME_ALICE)), showAvatarIndicator = false, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), @@ -409,7 +410,7 @@ internal fun HomeTopBarSpacesPreview() = ElementPreview { internal fun HomeTopBarWithIndicatorPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), + currentUserAndNeighbors = persistentListOf(aMatrixUser(id = "@id:domain", displayName = USER_NAME_ALICE)), showAvatarIndicator = true, areSearchResultsDisplayed = false, scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt index 83db9a9c61..3d7b8df618 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt @@ -31,6 +31,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB 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 @@ -116,10 +118,10 @@ internal fun LocationShareRowPreview() = ElementPreview { LocationShareRow( item = LocationShareItem( userId = UserId("@alice:matrix.org"), - displayName = "Alice", + displayName = USER_NAME_ALICE, avatarData = AvatarData( id = "@alice:matrix.org", - name = "Alice", + name = USER_NAME_ALICE, url = null, size = AvatarSize.UserListItem, ), @@ -133,10 +135,10 @@ internal fun LocationShareRowPreview() = ElementPreview { LocationShareRow( item = LocationShareItem( userId = UserId("@bob:matrix.org"), - displayName = "Bob", + displayName = USER_NAME_BOB, avatarData = AvatarData( id = "@bob:matrix.org", - name = "Bob", + name = USER_NAME_BOB, url = null, size = AvatarSize.UserListItem, ), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt index c93af5c162..1beabcd3fa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt @@ -43,6 +43,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.ROOM_NAME +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -303,7 +305,7 @@ internal fun ThreadsListViewPreview() { ThreadsListView( state = ThreadsListState( roomId = RoomId("!room-id:server"), - roomName = "Room name", + roomName = ROOM_NAME, roomAvatarUrl = null, threads = List(10) { aThreadListRowItem(threadId = ThreadId("\$thread-$it")) }.toImmutableList(), isRoomTombstoned = false, @@ -360,7 +362,7 @@ fun aThreadListItem( fun aThreadListItemEvent( threadId: ThreadId = ThreadId("\$a-thread-id"), senderId: UserId = UserId("@a-user-id:server"), - senderProfile: ProfileDetails = ProfileDetails.Ready(displayName = "Alice", displayNameAmbiguous = false, avatarUrl = null), + senderProfile: ProfileDetails = ProfileDetails.Ready(displayName = USER_NAME_ALICE, displayNameAmbiguous = false, avatarUrl = null), isOwn: Boolean = false, content: EventContent = MessageContent( body = "Hello world!", diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageShieldView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageShieldView.kt index 096ab018e2..1859e66750 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageShieldView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageShieldView.kt @@ -26,6 +26,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId @@ -162,7 +163,7 @@ internal fun MessageShieldViewPreview() { MessageShield.AuthenticityNotGuaranteed(false), forwarder = UserId("@alice:example.com"), forwarderProfile = ProfileDetails.Ready( - displayName = "Alice", + displayName = USER_NAME_ALICE, displayNameAmbiguous = false, avatarUrl = null, ), 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 f15d4d39a5..5785627564 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 @@ -92,6 +92,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.swipe.SwipeableActionsState import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState import io.element.android.libraries.designsystem.text.toPx @@ -863,7 +864,7 @@ internal fun TimelineItemEventRowWithThreadSummaryPreview() = ElementPreview { ), senderId = UserId("@user:id"), senderProfile = ProfileDetails.Ready( - displayName = "Alice", + displayName = USER_NAME_ALICE, avatarUrl = null, displayNameAmbiguous = false, ), @@ -898,7 +899,7 @@ internal fun ThreadSummaryViewPreview() { ), senderId = UserId("@user:id"), senderProfile = ProfileDetails.Ready( - displayName = "Alice", + displayName = USER_NAME_ALICE, avatarUrl = null, displayNameAmbiguous = true, ), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowUtdPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowUtdPreview.kt index 49328ac025..abacc45ef8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowUtdPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowUtdPreview.kt @@ -16,6 +16,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UtdCause @@ -25,7 +27,7 @@ internal fun TimelineItemEventRowUtdPreview() = ElementPreview { Column { ATimelineItemEventRow( event = aTimelineItemEvent( - senderDisplayName = "Alice", + senderDisplayName = USER_NAME_ALICE, isMine = false, content = TimelineItemEncryptedContent( data = UnableToDecryptContent.Data.MegolmV1AesSha2( @@ -39,7 +41,7 @@ internal fun TimelineItemEventRowUtdPreview() = ElementPreview { ) ATimelineItemEventRow( event = aTimelineItemEvent( - senderDisplayName = "Bob", + senderDisplayName = USER_NAME_BOB, isMine = false, content = TimelineItemEncryptedContent( data = UnableToDecryptContent.Data.MegolmV1AesSha2( @@ -54,7 +56,7 @@ internal fun TimelineItemEventRowUtdPreview() = ElementPreview { ATimelineItemEventRow( event = aTimelineItemEvent( - senderDisplayName = "Bob", + senderDisplayName = USER_NAME_BOB, isMine = false, content = TimelineItemEncryptedContent( data = UnableToDecryptContent.Data.MegolmV1AesSha2( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt index f812b40e61..d59526d8bb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.designsystem.text.toAnnotatedString import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.allBooleans @@ -86,13 +87,13 @@ internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview { ) TimelineItemRoomBeginningView( predecessorRoom = null, - roomName = "Room Name", + roomName = ROOM_NAME, isDm = isDm, onPredecessorRoomClick = {}, ) TimelineItemRoomBeginningView( predecessorRoom = PredecessorRoom(RoomId("!roomId:matrix.org")), - roomName = "Room Name", + roomName = ROOM_NAME, isDm = isDm, onPredecessorRoomClick = {}, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt index b1a31da443..639092fc6c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt @@ -43,6 +43,7 @@ import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text @@ -175,9 +176,9 @@ private fun RoomAvatarAndNameRow( internal fun MessagesViewTopBarPreview() = ElementPreview { @Composable fun AMessagesViewTopBar( - roomName: String? = "Room name", + roomName: String? = ROOM_NAME, roomAvatar: AvatarData = anAvatarData( - name = "Room name", + name = ROOM_NAME, size = AvatarSize.TimelineRoom, ), isTombstoned: Boolean = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt index 2247566531..e73b6b19b7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.ROOM_NAME import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -96,9 +97,9 @@ internal fun ThreadTopBar( internal fun ThreadTopBarPreview() = ElementPreview { @Composable fun AThreadTopBar( - roomName: String? = "Room name", + roomName: String? = ROOM_NAME, roomAvatarData: AvatarData = anAvatarData( - name = "Room name", + name = ROOM_NAME, size = AvatarSize.TimelineRoom, ), isTombstoned: Boolean = false, @@ -123,7 +124,7 @@ internal fun ThreadTopBarPreview() = ElementPreview { HorizontalDivider() AThreadTopBar( roomAvatarData = anAvatarData( - name = "Room name", + name = ROOM_NAME, url = "https://some-avatar.jpg", size = AvatarSize.TimelineRoom, ), diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt index 13f69a7e1e..2f238f9935 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfileStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.preferences.impl.user.editprofile import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.preview.USER_NAME_JOHN_DOE import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.permissions.api.PermissionsState @@ -28,7 +29,7 @@ open class EditUserProfileStateProvider : PreviewParameterProvider = emptyList(), saveButtonEnabled: Boolean = true, diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt index ac146a36a7..5ba0280b14 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text @@ -140,7 +141,7 @@ internal fun UserProfileHeaderSectionPreview() = ElementPreview { UserProfileHeaderSection( avatarUrl = null, userId = UserId("@alice:example.com"), - userName = "Alice", + userName = USER_NAME_ALICE, verificationState = UserProfileVerificationState.VERIFIED, openAvatarPreview = {}, onUserIdClick = {}, @@ -154,7 +155,7 @@ internal fun UserProfileHeaderSectionWithVerificationViolationPreview() = Elemen UserProfileHeaderSection( avatarUrl = null, userId = UserId("@alice:example.com"), - userName = "Alice", + userName = USER_NAME_ALICE, verificationState = UserProfileVerificationState.VERIFICATION_VIOLATION, openAvatarPreview = {}, onUserIdClick = {}, diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt index edaadc583d..c398d72ddd 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.UserId @@ -86,7 +87,7 @@ internal fun VerificationUserProfileContentPreview() = ElementPreview( VerificationUserProfileContent( user = MatrixUser( userId = UserId("@alice:example.com"), - displayName = "Alice", + displayName = USER_NAME_ALICE, avatarUrl = "https://example.com/avatar.png", ) ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LocationPin.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LocationPin.kt index 9e783d605c..4940f2f965 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LocationPin.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LocationPin.kt @@ -54,6 +54,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.utils.CommonDrawables private val PIN_WIDTH = 42.dp @@ -395,7 +396,7 @@ private object LocationPinRenderer { internal fun LocationPinPreview() = ElementPreview { val sampleAvatarData = AvatarData( id = "@alice:matrix.org", - name = "Alice", + name = USER_NAME_ALICE, url = null, size = AvatarSize.SelectedUser ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt index c5b870507b..f19796e053 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt @@ -29,6 +29,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.designsystem.text.toPx import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag @@ -116,12 +118,12 @@ internal fun DmAvatarsPreview() = ElementThemedPreview { DmAvatars( userAvatarData = anAvatarData( id = "Alice", - name = "Alice", + name = USER_NAME_ALICE, size = size, ), otherUserAvatarData = anAvatarData( id = "Bob", - name = "Bob", + name = USER_NAME_BOB, size = size, ), openAvatarPreview = {}, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt index 13747a1a3d..12fa54add1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/InviteSenderView.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.ui.model.InviteSender @@ -57,10 +58,10 @@ internal fun InviteSenderViewPreview() = ElementPreview { InviteSenderView( inviteSender = InviteSender( userId = UserId("@bob:example.com"), - displayName = "Bob", + displayName = USER_NAME_BOB, avatarData = AvatarData( id = "@bob:example.com", - name = "Bob", + name = USER_NAME_BOB, url = null, size = AvatarSize.InviteSender, ), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/OrganizationHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/OrganizationHeader.kt index 374ebd7a1a..0a1e3bdc87 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/OrganizationHeader.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/OrganizationHeader.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.SPACE_NAME /** * Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=3643-2048&m=dev @@ -72,7 +73,7 @@ internal fun OrganizationHeaderPreview() = ElementPreview { url = "anUrl", size = AvatarSize.OrganizationHeader, ), - name = "Space name", + name = SPACE_NAME, numberOfSpaces = 9, numberOfRooms = 88, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt index 97aa6ea613..7aee62902f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SpaceHeaderView.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarType import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.SPACE_NAME import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE import io.element.android.libraries.designsystem.preview.USER_NAME_BOB import io.element.android.libraries.designsystem.preview.USER_NAME_CHARLIE @@ -119,7 +120,7 @@ internal fun SpaceHeaderViewPreview() = ElementPreview { size = AvatarSize.SpaceHeader, ), alias = RoomAlias("#spaceAlias:matrix.org"), - name = "Space name", + name = SPACE_NAME, topic = "Space topic: " + LoremIpsum(40).values.first(), topicMaxLines = 2, visibility = SpaceRoomVisibility.Public, From 861de339d8360da99ccfcb0fdceb9d916e779a65 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 7 May 2026 08:43:48 +0000 Subject: [PATCH 104/471] Update screenshots --- ...ponents.virtual_TimelineItemRoomBeginningView_Day_0_en.png | 4 ++-- ...nents.virtual_TimelineItemRoomBeginningView_Night_0_en.png | 4 ++-- ...res.preferences.impl.root_PreferencesRootViewDark_1_en.png | 4 ++-- ...es.preferences.impl.root_PreferencesRootViewLight_1_en.png | 4 ++-- ...eatures.preferences.impl.user_UserPreferences_Day_1_en.png | 4 ++-- ...tures.preferences.impl.user_UserPreferences_Night_1_en.png | 4 ++-- ...braries.matrix.ui.components_MatrixUserHeader_Day_1_en.png | 4 ++-- ...aries.matrix.ui.components_MatrixUserHeader_Night_1_en.png | 4 ++-- .../libraries.matrix.ui.components_MatrixUserRow_Day_1_en.png | 4 ++-- ...ibraries.matrix.ui.components_MatrixUserRow_Night_1_en.png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png index 5815548686..8d62522c1e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abc1526f441c218d39e44ef3f146d4fee3bbb0628c4ece25fb2ae4ef7e4100b0 -size 48820 +oid sha256:13573edc4f14581b14a0238a66b6fdc040ccc02d49cd670c5e0ec1eb78b61bed +size 48621 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png index e01465633c..c61ea59d37 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20c52bb8f44c186d3104d457c5de9c0597d99f0a5be37a9d8680fb4be35d422f -size 53777 +oid sha256:d3153640c7d5f5be8bc66424cce167c44f48497bf5d91a25963a1e2d5e506d6f +size 53643 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 3ad13553de..b04da30f28 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:be4cbe72c73d76252f902e122bd62ba51ff89cf2cb20ddd61ed6615e983c65cf -size 25153 +oid sha256:0a6ddec2c9743976674657e454cb14bb479d774f1aa13882a34ceb89ff75b179 +size 24424 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 933a646f6b..a2d4d1b036 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:8d3a7ae9522da2911bf256273a4972020fc1677f026fbb9f17d27f549ab03ea3 -size 25978 +oid sha256:fa1a7c175f6551680b1a71ddaac14014f6503ccc2928d4ad97eced8a80626ed4 +size 25158 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_1_en.png index 74bc9743d7..3a1ad156a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b61c5a72e8e63a1775d20717c78d8e68e46c7c22b8e4ab8c24154dc3d48d7d0e -size 11428 +oid sha256:33482ad6e5033800e911dab44d4ba5bec15cfa58749102c5da4e2c57a8e760e8 +size 9783 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_1_en.png index 9413b91e88..81d4ceda60 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.user_UserPreferences_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4e1512cd7a58ede774755b6e3ac427e90c437bbbadaea8479506af776999c3 -size 11323 +oid sha256:e39bc143f7ee45c47ff57bfb53a773d7df2ba14568a9368643d8bab9dbbf4955 +size 9675 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_1_en.png index 74bc9743d7..3a1ad156a2 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b61c5a72e8e63a1775d20717c78d8e68e46c7c22b8e4ab8c24154dc3d48d7d0e -size 11428 +oid sha256:33482ad6e5033800e911dab44d4ba5bec15cfa58749102c5da4e2c57a8e760e8 +size 9783 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_1_en.png index 9413b91e88..81d4ceda60 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserHeader_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4e1512cd7a58ede774755b6e3ac427e90c437bbbadaea8479506af776999c3 -size 11323 +oid sha256:e39bc143f7ee45c47ff57bfb53a773d7df2ba14568a9368643d8bab9dbbf4955 +size 9675 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_1_en.png index 10d4beffb9..5174fbe9bf 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a992addeb8525c7df4be04514c82e22c8c1269d7c14aa25497efaac50e844a8 -size 9410 +oid sha256:00a060c5b66ee6febd8bfe145548aa3b2fa43ed0852e4224ab08a4641cfdc6b5 +size 8075 diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_1_en.png index 38dc8e36ba..fa48ce852e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.components_MatrixUserRow_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07b7d5515f0ca6e6634e8ff4b53c73f48b1170fe8d2488c7a8863d1568c52cdf -size 9420 +oid sha256:d23f245942d6353707c4252a05935b9d59a5a8ef380f351d0341b804413969ca +size 8101 From 61374bca4ef4b53f6fa048fc592f39879e1d0651 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 May 2026 18:24:25 +0200 Subject: [PATCH 105/471] Improve detection of configure PIN code. --- .../impl/biometric/BiometricAuthenticator.kt | 8 +++--- .../impl/pin/DefaultPinCodeManager.kt | 5 ++-- .../impl/storage/EncryptedPinCodeStorage.kt | 7 ----- .../storage/PreferencesLockScreenStore.kt | 6 ----- .../impl/DefaultLockScreenServiceTest.kt | 19 ++++++++----- .../biometric/FakeBiometricAuthenticator.kt | 2 +- .../pin/storage/InMemoryLockScreenStore.kt | 9 ------- libraries/cryptography/api/build.gradle.kts | 4 +++ .../cryptography/api/SecretKeyRepository.kt | 7 +++-- libraries/cryptography/impl/build.gradle.kts | 1 + .../impl/KeyStoreSecretKeyRepository.kt | 27 ++++++++++++++++--- libraries/cryptography/test/build.gradle.kts | 1 + .../test/SimpleSecretKeyRepository.kt | 25 ++++++++++++++--- 13 files changed, 77 insertions(+), 44 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt index a96c713ff2..d18d9b73b7 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticator.kt @@ -36,13 +36,13 @@ interface BiometricAuthenticator { } val isActive: Boolean - fun setup() + suspend fun setup() suspend fun authenticate(): AuthenticationResult } class NoopBiometricAuthentication : BiometricAuthenticator { override val isActive: Boolean = false - override fun setup() = Unit + override suspend fun setup() = Unit override suspend fun authenticate() = BiometricAuthenticator.AuthenticationResult.Failure() } @@ -58,7 +58,7 @@ class DefaultBiometricAuthentication( private var cryptoObject: CryptoObject? = null - override fun setup() { + override suspend fun setup() { try { val secretKey = ensureKey() val cipher = encryptionDecryptionService.createEncryptionCipher(secretKey) @@ -86,7 +86,7 @@ class DefaultBiometricAuthentication( } @Throws(KeyPermanentlyInvalidatedException::class) - private fun ensureKey() = secretKeyRepository.getOrCreateKey(keyAlias, true).also { + private suspend fun ensureKey() = secretKeyRepository.getOrCreateKey(keyAlias, true).also { encryptionDecryptionService.createEncryptionCipher(it) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index 091432044a..9a646461fa 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -18,7 +18,7 @@ import io.element.android.libraries.cryptography.api.SecretKeyRepository import kotlinx.coroutines.flow.Flow import java.util.concurrent.CopyOnWriteArrayList -private const val SECRET_KEY_ALIAS = "elementx.SECRET_KEY_ALIAS_PIN_CODE" +internal const val SECRET_KEY_ALIAS = "elementx.SECRET_KEY_ALIAS_PIN_CODE" @ContributesBinding(AppScope::class) @SingleIn(AppScope::class) @@ -38,7 +38,7 @@ class DefaultPinCodeManager( } override fun hasPinCode(): Flow { - return lockScreenStore.hasPinCode() + return secretKeyRepository.hasKey(SECRET_KEY_ALIAS) } override suspend fun getPinCodeSize(): Int { @@ -79,6 +79,7 @@ class DefaultPinCodeManager( override suspend fun deletePinCode() { lockScreenStore.deleteEncryptedPinCode() lockScreenStore.resetCounter() + secretKeyRepository.deleteKey(SECRET_KEY_ALIAS) callbacks.forEach { it.onPinCodeRemoved() } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt index c4558812de..b41e6a9578 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/EncryptedPinCodeStorage.kt @@ -8,8 +8,6 @@ package io.element.android.features.lockscreen.impl.storage -import kotlinx.coroutines.flow.Flow - /** * Should be implemented by any class that provides access to the encrypted PIN code. * All methods are suspending in case there are async IO operations involved. @@ -29,9 +27,4 @@ interface EncryptedPinCodeStorage { * Deletes the PIN code from some persistable storage. */ suspend fun deleteEncryptedPinCode() - - /** - * Returns whether the PIN code is stored or not. - */ - fun hasPinCode(): Flow } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt index 6b99d90592..98435f962d 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt @@ -70,12 +70,6 @@ class PreferencesLockScreenStore( } } - override fun hasPinCode(): Flow { - return dataStore.data.map { preferences -> - preferences[pinCodeKey] != null - } - } - override fun isBiometricUnlockAllowed(): Flow { return dataStore.data.map { preferences -> preferences[biometricUnlockKey] ?: false diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt index 9082f20a55..f906d0d6ba 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenServiceTest.kt @@ -14,9 +14,12 @@ import io.element.android.features.lockscreen.impl.biometric.BiometricAuthentica import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager import io.element.android.features.lockscreen.impl.fixtures.aLockScreenConfig import io.element.android.features.lockscreen.impl.pin.PinCodeManager +import io.element.android.features.lockscreen.impl.pin.SECRET_KEY_ALIAS import io.element.android.features.lockscreen.impl.pin.createDefaultPinCodeManager import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.libraries.cryptography.api.SecretKeyRepository +import io.element.android.libraries.cryptography.test.SimpleSecretKeyRepository import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver import io.element.android.services.appnavstate.api.AppForegroundStateService @@ -38,18 +41,18 @@ class DefaultLockScreenServiceTest { @Test fun `when the pin is mandatory, isSetupRequired emits true`() = runTest { - val lockScreenStore = InMemoryLockScreenStore() + val secretKeyRepository = SimpleSecretKeyRepository() val sut = createDefaultLockScreenService( lockScreenConfig = aLockScreenConfig(isPinMandatory = true), - lockScreenStore = lockScreenStore, + secretKeyRepository = secretKeyRepository, ) sut.isSetupRequired().test { assertThat(awaitItem()).isTrue() // When the user configures the pin code, the setup is not required anymore - lockScreenStore.saveEncryptedPinCode("encryptedCode") + secretKeyRepository.getOrCreateKey(SECRET_KEY_ALIAS, true) assertThat(awaitItem()).isFalse() // Users deletes the pin code - lockScreenStore.deleteEncryptedPinCode() + secretKeyRepository.deleteKey("elementx.SECRET_KEY_ALIAS_PIN_CODE") assertThat(awaitItem()).isTrue() } } @@ -57,16 +60,16 @@ class DefaultLockScreenServiceTest { @Test fun `when the last session is deleted, the pin code is removed`() = runTest { val sessionObserver = FakeSessionObserver() - val lockScreenStore = InMemoryLockScreenStore() + val secretKeyRepository = SimpleSecretKeyRepository() val sut = createDefaultLockScreenService( lockScreenConfig = aLockScreenConfig(isPinMandatory = true), - lockScreenStore = lockScreenStore, + secretKeyRepository = secretKeyRepository, sessionObserver = sessionObserver, ) sut.isPinSetup().test { assertThat(awaitItem()).isFalse() // When the user configure the pin code, the setup is not required anymore - lockScreenStore.saveEncryptedPinCode("encryptedCode") + secretKeyRepository.getOrCreateKey(SECRET_KEY_ALIAS, true) assertThat(awaitItem()).isTrue() sessionObserver.onSessionDeleted("userId", wasLastSession = false) expectNoEvents() @@ -79,8 +82,10 @@ class DefaultLockScreenServiceTest { private fun TestScope.createDefaultLockScreenService( lockScreenConfig: LockScreenConfig = aLockScreenConfig(), lockScreenStore: LockScreenStore = InMemoryLockScreenStore(), + secretKeyRepository: SecretKeyRepository = SimpleSecretKeyRepository(), pinCodeManager: PinCodeManager = createDefaultPinCodeManager( lockScreenStore = lockScreenStore, + secretKeyRepository = secretKeyRepository, ), sessionObserver: SessionObserver = FakeSessionObserver(), appForegroundStateService: AppForegroundStateService = FakeAppForegroundStateService(), diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticator.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticator.kt index 073bdc799d..63729f941a 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticator.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticator.kt @@ -12,6 +12,6 @@ class FakeBiometricAuthenticator( override val isActive: Boolean = false, private val authenticateLambda: suspend () -> BiometricAuthenticator.AuthenticationResult = { BiometricAuthenticator.AuthenticationResult.Success }, ) : BiometricAuthenticator { - override fun setup() = Unit + override suspend fun setup() = Unit override suspend fun authenticate() = authenticateLambda() } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt index 61acf71cdd..312a33b7f1 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/pin/storage/InMemoryLockScreenStore.kt @@ -15,12 +15,7 @@ import kotlinx.coroutines.flow.MutableStateFlow private const val DEFAULT_REMAINING_ATTEMPTS = 3 class InMemoryLockScreenStore : LockScreenStore { - private val hasPinCode = MutableStateFlow(false) private var pinCode: String? = null - set(value) { - field = value - hasPinCode.value = value != null - } private var remainingAttempts: Int = DEFAULT_REMAINING_ATTEMPTS private var isBiometricUnlockAllowed = MutableStateFlow(false) @@ -48,10 +43,6 @@ class InMemoryLockScreenStore : LockScreenStore { pinCode = null } - override fun hasPinCode(): Flow { - return hasPinCode - } - override fun isBiometricUnlockAllowed(): Flow { return isBiometricUnlockAllowed } diff --git a/libraries/cryptography/api/build.gradle.kts b/libraries/cryptography/api/build.gradle.kts index 9ce26419d8..74fc5f6ecc 100644 --- a/libraries/cryptography/api/build.gradle.kts +++ b/libraries/cryptography/api/build.gradle.kts @@ -13,3 +13,7 @@ plugins { android { namespace = "io.element.android.libraries.cryptography.api" } + +dependencies { + implementation(libs.coroutines.core) +} diff --git a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt index ba6c10dbe0..b210664d9f 100644 --- a/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt +++ b/libraries/cryptography/api/src/main/kotlin/io/element/android/libraries/cryptography/api/SecretKeyRepository.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.cryptography.api +import kotlinx.coroutines.flow.Flow import javax.crypto.SecretKey /** @@ -15,16 +16,18 @@ import javax.crypto.SecretKey * Implementation should be able to store the generated key securely. */ interface SecretKeyRepository { + fun hasKey(alias: String): Flow + /** * Get or create a secret key for a given alias. * @param alias the alias to use * @param requiresUserAuthentication true if the key should be protected by user authentication */ - fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey + suspend fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey /** * Delete the secret key for a given alias. * @param alias the alias to use */ - fun deleteKey(alias: String) + suspend fun deleteKey(alias: String) } diff --git a/libraries/cryptography/impl/build.gradle.kts b/libraries/cryptography/impl/build.gradle.kts index 454432de6f..3a1f55126e 100644 --- a/libraries/cryptography/impl/build.gradle.kts +++ b/libraries/cryptography/impl/build.gradle.kts @@ -21,6 +21,7 @@ setupDependencyInjection() dependencies { implementation(projects.libraries.di) + implementation(libs.coroutines.core) api(projects.libraries.cryptography.api) testCommonDependencies(libs) diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt index 46572ef047..dccba2a136 100644 --- a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt @@ -13,11 +13,16 @@ import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.SingleIn import io.element.android.libraries.cryptography.api.AESEncryptionSpecs import io.element.android.libraries.cryptography.api.SecretKeyRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import timber.log.Timber import java.security.KeyStore import java.security.KeyStoreException +import java.util.concurrent.ConcurrentHashMap import javax.crypto.KeyGenerator import javax.crypto.SecretKey @@ -25,13 +30,22 @@ import javax.crypto.SecretKey * Default implementation of [SecretKeyRepository] that uses the Android Keystore to store the keys. * The generated key uses AES algorithm, with a key size of 128 bits, and the GCM block mode. */ +@SingleIn(AppScope::class) @ContributesBinding(AppScope::class) class KeyStoreSecretKeyRepository( private val keyStore: KeyStore, ) : SecretKeyRepository { + private val hasKeyMap = ConcurrentHashMap>() + + override fun hasKey(alias: String): Flow { + return hasKeyMap.getOrPut(alias) { + MutableStateFlow(runCatching { keyStore.containsAlias(alias) }.getOrDefault(false)) + }.asStateFlow() + } + // False positive lint issue @SuppressLint("WrongConstant") - override fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey { + override suspend fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey { val secretKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.SecretKeyEntry) ?.secretKey return if (secretKeyEntry == null) { @@ -46,15 +60,22 @@ class KeyStoreSecretKeyRepository( .setUserAuthenticationRequired(requiresUserAuthentication) .build() generator.init(keyGenSpec) - generator.generateKey() + generator.generateKey().also { + hasKeyMap.getOrPut(alias) { + MutableStateFlow(true) + }.emit(true) + } } else { secretKeyEntry } } - override fun deleteKey(alias: String) { + override suspend fun deleteKey(alias: String) { try { keyStore.deleteEntry(alias) + hasKeyMap.getOrPut(alias) { + MutableStateFlow(false) + }.emit(false) } catch (e: KeyStoreException) { Timber.e(e) } diff --git a/libraries/cryptography/test/build.gradle.kts b/libraries/cryptography/test/build.gradle.kts index eaa621d53a..5cf04a9754 100644 --- a/libraries/cryptography/test/build.gradle.kts +++ b/libraries/cryptography/test/build.gradle.kts @@ -16,4 +16,5 @@ android { dependencies { api(projects.libraries.cryptography.api) + implementation(libs.coroutines.core) } diff --git a/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyRepository.kt b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyRepository.kt index 0e301553ea..507325f45b 100644 --- a/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyRepository.kt +++ b/libraries/cryptography/test/src/main/kotlin/io/element/android/libraries/cryptography/test/SimpleSecretKeyRepository.kt @@ -10,20 +10,39 @@ package io.element.android.libraries.cryptography.test import io.element.android.libraries.cryptography.api.AESEncryptionSpecs import io.element.android.libraries.cryptography.api.SecretKeyRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import java.util.concurrent.ConcurrentHashMap import javax.crypto.KeyGenerator import javax.crypto.SecretKey class SimpleSecretKeyRepository : SecretKeyRepository { private var secretKeyForAlias = HashMap() - override fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey { + private val hasKeyMap = ConcurrentHashMap>() + + override fun hasKey(alias: String): Flow { + return hasKeyMap.getOrPut(alias) { + MutableStateFlow(false) + }.asStateFlow() + } + + override suspend fun getOrCreateKey(alias: String, requiresUserAuthentication: Boolean): SecretKey { return secretKeyForAlias.getOrPut(alias) { - generateKey() + generateKey().also { + hasKeyMap.getOrPut(alias) { + MutableStateFlow(true) + }.emit(true) + } } } - override fun deleteKey(alias: String) { + override suspend fun deleteKey(alias: String) { secretKeyForAlias.remove(alias) + hasKeyMap.getOrPut(alias) { + MutableStateFlow(false) + }.emit(false) } private fun generateKey(): SecretKey { From 8799dda4719ecfac931e57cee7bf8c57203c9bf1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:36:00 +0200 Subject: [PATCH 106/471] Force sign out if PIN code store is corrupted. --- .../lockscreen/impl/pin/DefaultPinCodeManager.kt | 4 ++-- .../features/lockscreen/impl/pin/PinCodeManager.kt | 4 ++-- .../lockscreen/impl/unlock/PinUnlockPresenter.kt | 8 +++++++- .../features/lockscreen/impl/unlock/PinUnlockState.kt | 10 +++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index 9a646461fa..2bda5759a8 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -41,8 +41,8 @@ class DefaultPinCodeManager( return secretKeyRepository.hasKey(SECRET_KEY_ALIAS) } - override suspend fun getPinCodeSize(): Int { - val encryptedPinCode = lockScreenStore.getEncryptedCode() ?: return 0 + override suspend fun getPinCodeSize(): Int? { + val encryptedPinCode = lockScreenStore.getEncryptedCode() ?: return null val secretKey = secretKeyRepository.getOrCreateKey(SECRET_KEY_ALIAS, false) val decryptedPinCode = encryptionDecryptionService.decrypt(secretKey, EncryptionResult.fromBase64(encryptedPinCode)) return decryptedPinCode.size diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt index 9282f3e7df..350631a233 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/PinCodeManager.kt @@ -51,9 +51,9 @@ interface PinCodeManager { fun hasPinCode(): Flow /** - * @return the size of the saved pin code. + * @return the size of the saved pin code. Return null if no pin code is saved. */ - suspend fun getPinCodeSize(): Int + suspend fun getPinCodeSize(): Int? /** * Creates a new encrypted pin code. diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index 5429320fc7..05b4dbfe72 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -69,7 +69,13 @@ class PinUnlockPresenter( LaunchedEffect(Unit) { suspend { val pinCodeSize = pinCodeManager.getPinCodeSize() - PinEntry.createEmpty(pinCodeSize) + if (pinCodeSize == null) { + // No pin code set, deleted store? Force sign out + showSignOutPrompt = true + throw Exception("No pin code size found") + } else { + PinEntry.createEmpty(pinCodeSize) + } }.runCatchingUpdatingState(pinEntryState) } LaunchedEffect(biometricUnlock) { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt index 2bbcbe335c..50d03045ab 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt @@ -25,9 +25,13 @@ data class PinUnlockState( val biometricUnlockResult: BiometricAuthenticator.AuthenticationResult?, val eventSink: (PinUnlockEvents) -> Unit ) { - val isSignOutPromptCancellable = when (remainingAttempts) { - is AsyncData.Success -> remainingAttempts.data > 0 - else -> true + val isSignOutPromptCancellable = if (pinEntry.isFailure()) { + false + } else { + when (remainingAttempts) { + is AsyncData.Success -> remainingAttempts.data > 0 + else -> true + } } val biometricUnlockErrorMessage = when { From 94d37c68d5559277bf932735d357ce93614e282d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:38:59 +0200 Subject: [PATCH 107/471] Use test extension. --- .../biometric/SetupBiometricPresenterTest.kt | 16 ++++------------ .../impl/setup/pin/SetupPinPresenterTest.kt | 8 ++------ .../impl/unlock/PinUnlockPresenterTest.kt | 16 ++++------------ 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt index 3f87c1dccf..458cd295f6 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt @@ -8,9 +8,6 @@ package io.element.android.features.lockscreen.impl.setup.biometric -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticator import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager @@ -18,6 +15,7 @@ import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthen import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore import io.element.android.features.lockscreen.impl.storage.LockScreenStore +import io.element.android.tests.testutils.test import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Test @@ -30,9 +28,7 @@ class SetupBiometricPresenterTest { FakeBiometricAuthenticator(authenticateLambda = { BiometricAuthenticator.AuthenticationResult.Success }) }) val presenter = createSetupBiometricPresenter(lockScreenStore, fakeBiometricAuthenticatorManager) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() state.eventSink(SetupBiometricEvents.AllowBiometric) @@ -51,9 +47,7 @@ class SetupBiometricPresenterTest { FakeBiometricAuthenticator(authenticateLambda = { BiometricAuthenticator.AuthenticationResult.Failure() }) }) val presenter = createSetupBiometricPresenter(lockScreenStore, fakeBiometricAuthenticatorManager) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() state.eventSink(SetupBiometricEvents.AllowBiometric) @@ -66,9 +60,7 @@ class SetupBiometricPresenterTest { fun `present - skip flow`() = runTest { val lockScreenStore = InMemoryLockScreenStore() val presenter = createSetupBiometricPresenter(lockScreenStore) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() state.eventSink(SetupBiometricEvents.UsePin) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt index 6a1d32e879..46591eb2af 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt @@ -8,9 +8,6 @@ package io.element.android.features.lockscreen.impl.setup.pin -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.LockScreenConfig import io.element.android.features.lockscreen.impl.fixtures.aLockScreenConfig @@ -24,6 +21,7 @@ import io.element.android.features.lockscreen.impl.setup.pin.validation.SetupPin import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.test import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import org.junit.Test @@ -43,9 +41,7 @@ class SetupPinPresenterTest { } } val presenter = createSetupPinPresenter(callback) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().also { state -> state.choosePinEntry.assertEmpty() state.confirmPinEntry.assertEmpty() diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index f5bfb11818..197f45f0f8 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -8,9 +8,6 @@ package io.element.android.features.lockscreen.impl.unlock -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticatorManager import io.element.android.features.lockscreen.impl.biometric.FakeBiometricAuthenticatorManager @@ -25,6 +22,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -36,9 +34,7 @@ class PinUnlockPresenterTest { @Test fun `present - success verify flow`() = runTest { val presenter = createPinUnlockPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { awaitItem().also { state -> assertThat(state.pinEntry).isInstanceOf(AsyncData.Uninitialized::class.java) assertThat(state.showWrongPinTitle).isFalse() @@ -73,9 +69,7 @@ class PinUnlockPresenterTest { @Test fun `present - failure verify flow`() = runTest { val presenter = createPinUnlockPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) val initialState = awaitItem().also { state -> assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) @@ -102,9 +96,7 @@ class PinUnlockPresenterTest { val signOutLambda = lambdaRecorder {} val signOut = FakeLogoutUseCase(signOutLambda) val presenter = createPinUnlockPresenter(logoutUseCase = signOut) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { skipItems(1) awaitItem().also { state -> assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) From bc60512e102da8544d0eaa5f4d44a1dfafd17fd6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:39:52 +0200 Subject: [PATCH 108/471] `LockScreenSettingsEvents` -> `LockScreenSettingsEvent` --- ...ettingsEvents.kt => LockScreenSettingsEvent.kt} | 10 +++++----- .../impl/settings/LockScreenSettingsPresenter.kt | 10 +++++----- .../impl/settings/LockScreenSettingsState.kt | 2 +- .../impl/settings/LockScreenSettingsView.kt | 8 ++++---- .../settings/LockScreenSettingsPresenterTest.kt | 14 +++++++------- 5 files changed, 22 insertions(+), 22 deletions(-) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/{LockScreenSettingsEvents.kt => LockScreenSettingsEvent.kt} (62%) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvent.kt similarity index 62% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvent.kt index 2d62427e02..c7437912eb 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsEvent.kt @@ -8,9 +8,9 @@ package io.element.android.features.lockscreen.impl.settings -sealed interface LockScreenSettingsEvents { - data object OnRemovePin : LockScreenSettingsEvents - data object ConfirmRemovePin : LockScreenSettingsEvents - data object CancelRemovePin : LockScreenSettingsEvents - data object ToggleBiometricAllowed : LockScreenSettingsEvents +sealed interface LockScreenSettingsEvent { + data object OnRemovePin : LockScreenSettingsEvent + data object ConfirmRemovePin : LockScreenSettingsEvent + data object CancelRemovePin : LockScreenSettingsEvent + data object ToggleBiometricAllowed : LockScreenSettingsEvent } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt index 589794bde0..83a0253f39 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt @@ -51,10 +51,10 @@ class LockScreenSettingsPresenter( val biometricUnlock = biometricAuthenticatorManager.rememberConfirmBiometricAuthenticator() - fun handleEvent(event: LockScreenSettingsEvents) { + fun handleEvent(event: LockScreenSettingsEvent) { when (event) { - LockScreenSettingsEvents.CancelRemovePin -> showRemovePinConfirmation = false - LockScreenSettingsEvents.ConfirmRemovePin -> { + LockScreenSettingsEvent.CancelRemovePin -> showRemovePinConfirmation = false + LockScreenSettingsEvent.ConfirmRemovePin -> { coroutineScope.launch { if (showRemovePinConfirmation) { showRemovePinConfirmation = false @@ -62,8 +62,8 @@ class LockScreenSettingsPresenter( } } } - LockScreenSettingsEvents.OnRemovePin -> showRemovePinConfirmation = true - LockScreenSettingsEvents.ToggleBiometricAllowed -> { + LockScreenSettingsEvent.OnRemovePin -> showRemovePinConfirmation = true + LockScreenSettingsEvent.ToggleBiometricAllowed -> { coroutineScope.launch { if (!isBiometricEnabled) { biometricUnlock.setup() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsState.kt index a69d633508..62b8d6d4ee 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsState.kt @@ -13,5 +13,5 @@ data class LockScreenSettingsState( val isBiometricEnabled: Boolean, val showRemovePinConfirmation: Boolean, val showToggleBiometric: Boolean, - val eventSink: (LockScreenSettingsEvents) -> Unit + val eventSink: (LockScreenSettingsEvent) -> Unit ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt index fe5f20da0d..e78a5ee002 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsView.kt @@ -51,7 +51,7 @@ fun LockScreenSettingsView( }, style = ListItemStyle.Destructive, onClick = { - state.eventSink(LockScreenSettingsEvents.OnRemovePin) + state.eventSink(LockScreenSettingsEvent.OnRemovePin) } ) } @@ -61,7 +61,7 @@ fun LockScreenSettingsView( title = stringResource(id = R.string.screen_app_lock_settings_enable_biometric_unlock), isChecked = state.isBiometricEnabled, onCheckedChange = { - state.eventSink(LockScreenSettingsEvents.ToggleBiometricAllowed) + state.eventSink(LockScreenSettingsEvent.ToggleBiometricAllowed) } ) } @@ -72,10 +72,10 @@ fun LockScreenSettingsView( title = stringResource(id = R.string.screen_app_lock_settings_remove_pin_alert_title), content = stringResource(id = R.string.screen_app_lock_settings_remove_pin_alert_message), onSubmitClick = { - state.eventSink(LockScreenSettingsEvents.ConfirmRemovePin) + state.eventSink(LockScreenSettingsEvent.ConfirmRemovePin) }, onDismiss = { - state.eventSink(LockScreenSettingsEvents.CancelRemovePin) + state.eventSink(LockScreenSettingsEvent.CancelRemovePin) } ) } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenterTest.kt index ef3e94f27f..85eb4a37e7 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenterTest.kt @@ -43,19 +43,19 @@ class LockScreenSettingsPresenterTest { consumeItemsUntilPredicate { state -> state.showRemovePinOption }.last().also { state -> - state.eventSink(LockScreenSettingsEvents.OnRemovePin) + state.eventSink(LockScreenSettingsEvent.OnRemovePin) } awaitLastSequentialItem().also { state -> assertThat(state.showRemovePinConfirmation).isTrue() - state.eventSink(LockScreenSettingsEvents.CancelRemovePin) + state.eventSink(LockScreenSettingsEvent.CancelRemovePin) } awaitLastSequentialItem().also { state -> assertThat(state.showRemovePinConfirmation).isFalse() - state.eventSink(LockScreenSettingsEvents.OnRemovePin) + state.eventSink(LockScreenSettingsEvent.OnRemovePin) } awaitLastSequentialItem().also { state -> assertThat(state.showRemovePinConfirmation).isTrue() - state.eventSink(LockScreenSettingsEvents.ConfirmRemovePin) + state.eventSink(LockScreenSettingsEvent.ConfirmRemovePin) } consumeItemsUntilPredicate { it.showRemovePinOption.not() @@ -93,7 +93,7 @@ class LockScreenSettingsPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(LockScreenSettingsEvents.ToggleBiometricAllowed) + state.eventSink(LockScreenSettingsEvent.ToggleBiometricAllowed) } awaitItem().also { state -> assertThat(state.isBiometricEnabled).isTrue() @@ -114,7 +114,7 @@ class LockScreenSettingsPresenterTest { presenter.test { skipItems(1) awaitItem().also { state -> - state.eventSink(LockScreenSettingsEvents.ToggleBiometricAllowed) + state.eventSink(LockScreenSettingsEvent.ToggleBiometricAllowed) } } } @@ -137,7 +137,7 @@ class LockScreenSettingsPresenterTest { skipItems(1) awaitItem().also { state -> assertThat(state.isBiometricEnabled).isTrue() - state.eventSink(LockScreenSettingsEvents.ToggleBiometricAllowed) + state.eventSink(LockScreenSettingsEvent.ToggleBiometricAllowed) } awaitItem().also { state -> assertThat(state.isBiometricEnabled).isFalse() From 90918cbb9dba1f6124c569f8fc58125b0de36144 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:40:14 +0200 Subject: [PATCH 109/471] `SetupBiometricEvents` -> `SetupBiometricEvent` --- .../{SetupBiometricEvents.kt => SetupBiometricEvent.kt} | 6 +++--- .../impl/setup/biometric/SetupBiometricPresenter.kt | 6 +++--- .../lockscreen/impl/setup/biometric/SetupBiometricState.kt | 2 +- .../lockscreen/impl/setup/biometric/SetupBiometricView.kt | 6 +++--- .../impl/setup/biometric/SetupBiometricPresenterTest.kt | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/{SetupBiometricEvents.kt => SetupBiometricEvent.kt} (68%) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvent.kt similarity index 68% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvent.kt index ab8b18642e..d4db46b731 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.lockscreen.impl.setup.biometric -sealed interface SetupBiometricEvents { - data object AllowBiometric : SetupBiometricEvents - data object UsePin : SetupBiometricEvents +sealed interface SetupBiometricEvent { + data object AllowBiometric : SetupBiometricEvent + data object UsePin : SetupBiometricEvent } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt index 3af2a28851..ce914320bc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenter.kt @@ -35,16 +35,16 @@ class SetupBiometricPresenter( val coroutineScope = rememberCoroutineScope() val biometricUnlock = biometricAuthenticatorManager.rememberConfirmBiometricAuthenticator() - fun handleEvent(event: SetupBiometricEvents) { + fun handleEvent(event: SetupBiometricEvent) { when (event) { - SetupBiometricEvents.AllowBiometric -> coroutineScope.launch { + SetupBiometricEvent.AllowBiometric -> coroutineScope.launch { biometricUnlock.setup() if (biometricUnlock.authenticate() == BiometricAuthenticator.AuthenticationResult.Success) { lockScreenStore.setIsBiometricUnlockAllowed(true) isBiometricSetupDone = true } } - SetupBiometricEvents.UsePin -> coroutineScope.launch { + SetupBiometricEvent.UsePin -> coroutineScope.launch { lockScreenStore.setIsBiometricUnlockAllowed(false) isBiometricSetupDone = true } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricState.kt index 2843c028d1..db11b1dc30 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricState.kt @@ -10,5 +10,5 @@ package io.element.android.features.lockscreen.impl.setup.biometric data class SetupBiometricState( val isBiometricSetupDone: Boolean, - val eventSink: (SetupBiometricEvents) -> Unit + val eventSink: (SetupBiometricEvent) -> Unit ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt index 35b1ec76c0..70a1046e36 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricView.kt @@ -33,7 +33,7 @@ fun SetupBiometricView( modifier: Modifier = Modifier, ) { BackHandler { - state.eventSink(SetupBiometricEvents.UsePin) + state.eventSink(SetupBiometricEvent.UsePin) } HeaderFooterPage( modifier = modifier.padding(top = 80.dp), @@ -42,8 +42,8 @@ fun SetupBiometricView( }, footer = { SetupBiometricFooter( - onAllowClick = { state.eventSink(SetupBiometricEvents.AllowBiometric) }, - onSkipClick = { state.eventSink(SetupBiometricEvents.UsePin) } + onAllowClick = { state.eventSink(SetupBiometricEvent.AllowBiometric) }, + onSkipClick = { state.eventSink(SetupBiometricEvent.UsePin) } ) }, ) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt index 458cd295f6..9dde220906 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricPresenterTest.kt @@ -31,7 +31,7 @@ class SetupBiometricPresenterTest { presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() - state.eventSink(SetupBiometricEvents.AllowBiometric) + state.eventSink(SetupBiometricEvent.AllowBiometric) } awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isTrue() @@ -50,7 +50,7 @@ class SetupBiometricPresenterTest { presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() - state.eventSink(SetupBiometricEvents.AllowBiometric) + state.eventSink(SetupBiometricEvent.AllowBiometric) } } assertThat(lockScreenStore.isBiometricUnlockAllowed().first()).isFalse() @@ -63,7 +63,7 @@ class SetupBiometricPresenterTest { presenter.test { awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isFalse() - state.eventSink(SetupBiometricEvents.UsePin) + state.eventSink(SetupBiometricEvent.UsePin) } awaitItem().also { state -> assertThat(state.isBiometricSetupDone).isTrue() From 668b03f8bc8e24754f4c97fd58b7b009a132927d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:40:32 +0200 Subject: [PATCH 110/471] `SetupPinEvents` -> `SetupPinEvent` --- .../impl/setup/pin/{SetupPinEvents.kt => SetupPinEvent.kt} | 6 +++--- .../features/lockscreen/impl/setup/pin/SetupPinPresenter.kt | 6 +++--- .../features/lockscreen/impl/setup/pin/SetupPinState.kt | 2 +- .../features/lockscreen/impl/setup/pin/SetupPinView.kt | 4 ++-- .../lockscreen/impl/setup/pin/SetupPinPresenterTest.kt | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/{SetupPinEvents.kt => SetupPinEvent.kt} (74%) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvent.kt similarity index 74% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvent.kt index 276a94b2fc..f0dfdc33f0 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinEvent.kt @@ -8,7 +8,7 @@ package io.element.android.features.lockscreen.impl.setup.pin -sealed interface SetupPinEvents { - data class OnPinEntryChanged(val entryAsText: String, val fromConfirmationStep: Boolean) : SetupPinEvents - data object ClearFailure : SetupPinEvents +sealed interface SetupPinEvent { + data class OnPinEntryChanged(val entryAsText: String, val fromConfirmationStep: Boolean) : SetupPinEvent + data object ClearFailure : SetupPinEvent } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt index ac5b5bd1cc..d780927d44 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenter.kt @@ -74,9 +74,9 @@ class SetupPinPresenter( } } - fun handleEvent(event: SetupPinEvents) { + fun handleEvent(event: SetupPinEvent) { when (event) { - is SetupPinEvents.OnPinEntryChanged -> { + is SetupPinEvent.OnPinEntryChanged -> { // Use the fromConfirmationStep flag from ui to avoid race condition. if (event.fromConfirmationStep) { confirmPinEntry = confirmPinEntry.fillWith(event.entryAsText) @@ -84,7 +84,7 @@ class SetupPinPresenter( choosePinEntry = choosePinEntry.fillWith(event.entryAsText) } } - SetupPinEvents.ClearFailure -> { + SetupPinEvent.ClearFailure -> { when (setupPinFailure) { is SetupPinFailure.PinsDoNotMatch -> { choosePinEntry = choosePinEntry.clear() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinState.kt index 2d5124d440..cf65e63c1b 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinState.kt @@ -17,7 +17,7 @@ data class SetupPinState( val isConfirmationStep: Boolean, val setupPinFailure: SetupPinFailure?, val appName: String, - val eventSink: (SetupPinEvents) -> Unit + val eventSink: (SetupPinEvent) -> Unit ) { val activePinEntry = if (isConfirmationStep) { confirmPinEntry diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt index 5f2320db32..508d3c1fbb 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinView.kt @@ -107,7 +107,7 @@ private fun SetupPinContent( pinEntry = state.activePinEntry, isSecured = true, onValueChange = { entry -> - state.eventSink(SetupPinEvents.OnPinEntryChanged(entry, state.isConfirmationStep)) + state.eventSink(SetupPinEvent.OnPinEntryChanged(entry, state.isConfirmationStep)) }, modifier = Modifier .focusRequester(focusRequester) @@ -119,7 +119,7 @@ private fun SetupPinContent( title = state.setupPinFailure.title(), content = state.setupPinFailure.content(), onSubmit = { - state.eventSink(SetupPinEvents.ClearFailure) + state.eventSink(SetupPinEvent.ClearFailure) } ) } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt index 46591eb2af..9d63f9e26b 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/setup/pin/SetupPinPresenterTest.kt @@ -59,7 +59,7 @@ class SetupPinPresenterTest { awaitLastSequentialItem().also { state -> state.choosePinEntry.assertText(forbiddenPin) assertThat(state.setupPinFailure).isEqualTo(SetupPinFailure.ForbiddenPin) - state.eventSink(SetupPinEvents.ClearFailure) + state.eventSink(SetupPinEvent.ClearFailure) } awaitLastSequentialItem().also { state -> state.choosePinEntry.assertEmpty() @@ -78,7 +78,7 @@ class SetupPinPresenterTest { state.choosePinEntry.assertText(completePin) state.confirmPinEntry.assertText(mismatchedPin) assertThat(state.setupPinFailure).isEqualTo(SetupPinFailure.PinsDoNotMatch) - state.eventSink(SetupPinEvents.ClearFailure) + state.eventSink(SetupPinEvent.ClearFailure) } awaitLastSequentialItem().also { state -> state.choosePinEntry.assertEmpty() @@ -104,7 +104,7 @@ class SetupPinPresenterTest { } private fun SetupPinState.onPinEntryChanged(pinEntry: String) { - eventSink(SetupPinEvents.OnPinEntryChanged(pinEntry, isConfirmationStep)) + eventSink(SetupPinEvent.OnPinEntryChanged(pinEntry, isConfirmationStep)) } private fun createSetupPinPresenter( From 44baa4a383381ebfe97a7338a289c54c8e2d65ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 10:41:02 +0200 Subject: [PATCH 111/471] `PinUnlockEvents` -> `PinUnlockEvent` --- .../{PinUnlockEvents.kt => PinUnlockEvent.kt} | 16 +++++----- .../impl/unlock/PinUnlockPresenter.kt | 16 +++++----- .../lockscreen/impl/unlock/PinUnlockState.kt | 2 +- .../lockscreen/impl/unlock/PinUnlockView.kt | 16 +++++----- .../impl/unlock/PinUnlockPresenterTest.kt | 30 +++++++++---------- 5 files changed, 40 insertions(+), 40 deletions(-) rename features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/{PinUnlockEvents.kt => PinUnlockEvent.kt} (61%) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvent.kt similarity index 61% rename from features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt rename to features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvent.kt index bd9043859f..aa96a2e115 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvents.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockEvent.kt @@ -10,12 +10,12 @@ package io.element.android.features.lockscreen.impl.unlock import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel -sealed interface PinUnlockEvents { - data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvents - data class OnPinEntryChanged(val entryAsText: String) : PinUnlockEvents - data object OnForgetPin : PinUnlockEvents - data object ClearSignOutPrompt : PinUnlockEvents - data object SignOut : PinUnlockEvents - data object OnUseBiometric : PinUnlockEvents - data object ClearBiometricError : PinUnlockEvents +sealed interface PinUnlockEvent { + data class OnPinKeypadPressed(val pinKeypadModel: PinKeypadModel) : PinUnlockEvent + data class OnPinEntryChanged(val entryAsText: String) : PinUnlockEvent + data object OnForgetPin : PinUnlockEvent + data object ClearSignOutPrompt : PinUnlockEvent + data object SignOut : PinUnlockEvent + data object OnUseBiometric : PinUnlockEvent + data object ClearBiometricError : PinUnlockEvent } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index 05b4dbfe72..a6e35b6967 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -101,28 +101,28 @@ class PinUnlockPresenter( isUnlocked.value = true } - fun handleEvent(event: PinUnlockEvents) { + fun handleEvent(event: PinUnlockEvent) { when (event) { - is PinUnlockEvents.OnPinKeypadPressed -> { + is PinUnlockEvent.OnPinKeypadPressed -> { pinEntryState.value = pinEntry.process(event.pinKeypadModel) } - PinUnlockEvents.OnForgetPin -> showSignOutPrompt = true - PinUnlockEvents.ClearSignOutPrompt -> showSignOutPrompt = false - PinUnlockEvents.SignOut -> { + PinUnlockEvent.OnForgetPin -> showSignOutPrompt = true + PinUnlockEvent.ClearSignOutPrompt -> showSignOutPrompt = false + PinUnlockEvent.SignOut -> { if (showSignOutPrompt) { showSignOutPrompt = false coroutineScope.signOut(signOutAction) } } - PinUnlockEvents.OnUseBiometric -> { + PinUnlockEvent.OnUseBiometric -> { coroutineScope.launch { biometricUnlockResult = biometricUnlock.authenticate() } } - PinUnlockEvents.ClearBiometricError -> { + PinUnlockEvent.ClearBiometricError -> { biometricUnlockResult = null } - is PinUnlockEvents.OnPinEntryChanged -> { + is PinUnlockEvent.OnPinEntryChanged -> { pinEntryState.value = pinEntry.process(event.entryAsText) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt index 50d03045ab..037aa87dec 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockState.kt @@ -23,7 +23,7 @@ data class PinUnlockState( val showBiometricUnlock: Boolean, val isUnlocked: Boolean, val biometricUnlockResult: BiometricAuthenticator.AuthenticationResult?, - val eventSink: (PinUnlockEvents) -> Unit + val eventSink: (PinUnlockEvent) -> Unit ) { val isSignOutPromptCancellable = if (pinEntry.isFailure()) { false diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 659f8c2966..465f015cdd 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -69,7 +69,7 @@ fun PinUnlockView( ) { OnLifecycleEvent { _, event -> when (event) { - Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(PinUnlockEvents.OnUseBiometric) + Lifecycle.Event.ON_RESUME -> state.eventSink.invoke(PinUnlockEvent.OnUseBiometric) else -> Unit } } @@ -78,8 +78,8 @@ fun PinUnlockView( if (state.showSignOutPrompt) { SignOutPrompt( isCancellable = state.isSignOutPromptCancellable, - onSignOut = { state.eventSink(PinUnlockEvents.SignOut) }, - onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, + onSignOut = { state.eventSink(PinUnlockEvent.SignOut) }, + onDismiss = { state.eventSink(PinUnlockEvent.ClearSignOutPrompt) }, ) } when (state.signOutAction) { @@ -95,7 +95,7 @@ fun PinUnlockView( if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", - onSubmit = { state.eventSink(PinUnlockEvents.ClearBiometricError) } + onSubmit = { state.eventSink(PinUnlockEvent.ClearBiometricError) } ) } } @@ -125,10 +125,10 @@ private fun PinUnlockPage( modifier = Modifier.padding(top = 24.dp), showBiometricUnlock = state.showBiometricUnlock, onUseBiometric = { - state.eventSink(PinUnlockEvents.OnUseBiometric) + state.eventSink(PinUnlockEvent.OnUseBiometric) }, onForgotPin = { - state.eventSink(PinUnlockEvents.OnForgetPin) + state.eventSink(PinUnlockEvent.OnForgetPin) }, ) } @@ -144,7 +144,7 @@ private fun PinUnlockPage( pinEntry = pinEntry, isSecured = true, onValueChange = { - state.eventSink(PinUnlockEvents.OnPinEntryChanged(it)) + state.eventSink(PinUnlockEvent.OnPinEntryChanged(it)) }, modifier = Modifier .focusRequester(focusRequester) @@ -154,7 +154,7 @@ private fun PinUnlockPage( } else { PinKeypad( onClick = { - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(it)) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(it)) }, maxWidth = constraints.maxWidth, maxHeight = constraints.maxHeight, diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 197f45f0f8..4f93eba12e 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -46,17 +46,17 @@ class PinUnlockPresenterTest { awaitItem().also { state -> assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1'))) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2'))) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('1'))) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('2'))) } skipItems(1) awaitItem().also { state -> state.pinEntry.assertText(halfCompletePin) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Back)) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Empty)) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) - state.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('5'))) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3'))) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Back)) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Empty)) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3'))) + state.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('5'))) } skipItems(4) awaitItem().also { state -> @@ -77,10 +77,10 @@ class PinUnlockPresenterTest { } val numberOfAttempts = initialState.remainingAttempts.dataOrNull() ?: 0 repeat(numberOfAttempts) { - initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('1'))) - initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('2'))) - initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('3'))) - initialState.eventSink(PinUnlockEvents.OnPinKeypadPressed(PinKeypadModel.Number('4'))) + initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('1'))) + initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('2'))) + initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('3'))) + initialState.eventSink(PinUnlockEvent.OnPinKeypadPressed(PinKeypadModel.Number('4'))) } skipItems(4 * numberOfAttempts + 2) awaitItem().also { state -> @@ -101,20 +101,20 @@ class PinUnlockPresenterTest { awaitItem().also { state -> assertThat(state.pinEntry).isInstanceOf(AsyncData.Success::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Success::class.java) - state.eventSink(PinUnlockEvents.OnForgetPin) + state.eventSink(PinUnlockEvent.OnForgetPin) } awaitItem().also { state -> assertThat(state.showSignOutPrompt).isTrue() assertThat(state.isSignOutPromptCancellable).isTrue() - state.eventSink(PinUnlockEvents.ClearSignOutPrompt) + state.eventSink(PinUnlockEvent.ClearSignOutPrompt) } awaitItem().also { state -> assertThat(state.showSignOutPrompt).isFalse() - state.eventSink(PinUnlockEvents.OnForgetPin) + state.eventSink(PinUnlockEvent.OnForgetPin) } awaitItem().also { state -> assertThat(state.showSignOutPrompt).isTrue() - state.eventSink(PinUnlockEvents.SignOut) + state.eventSink(PinUnlockEvent.SignOut) } skipItems(2) awaitItem().also { state -> From 5269b9787e087765bb038aa62928e2fd62240708 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 11:25:03 +0200 Subject: [PATCH 112/471] Fix quality issue. --- .../features/lockscreen/impl/unlock/PinUnlockPresenter.kt | 2 +- .../libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt index a6e35b6967..c8dd8916f9 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenter.kt @@ -72,7 +72,7 @@ class PinUnlockPresenter( if (pinCodeSize == null) { // No pin code set, deleted store? Force sign out showSignOutPrompt = true - throw Exception("No pin code size found") + error("No pin code size found") } else { PinEntry.createEmpty(pinCodeSize) } diff --git a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt index dccba2a136..bcd38695c4 100644 --- a/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt +++ b/libraries/cryptography/impl/src/main/kotlin/io/element/android/libraries/cryptography/impl/KeyStoreSecretKeyRepository.kt @@ -37,6 +37,7 @@ class KeyStoreSecretKeyRepository( ) : SecretKeyRepository { private val hasKeyMap = ConcurrentHashMap>() + @Suppress("RunCatchingNotAllowed") override fun hasKey(alias: String): Flow { return hasKeyMap.getOrPut(alias) { MutableStateFlow(runCatching { keyStore.containsAlias(alias) }.getOrDefault(false)) From d558371798d3fc97cfc08b3d14ce4d2d802cdb0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 11:27:41 +0200 Subject: [PATCH 113/471] Add test. --- .../impl/unlock/PinUnlockPresenterTest.kt | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt index 4f93eba12e..fa7d05b5cb 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockPresenterTest.kt @@ -16,6 +16,7 @@ import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCall import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.pin.model.assertText +import io.element.android.features.lockscreen.impl.pin.storage.InMemoryLockScreenStore import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypadModel import io.element.android.features.logout.test.FakeLogoutUseCase import io.element.android.libraries.architecture.AsyncAction @@ -124,6 +125,28 @@ class PinUnlockPresenterTest { } } + @Test + fun `present - pin is configured, but deleted in store, sign out prompt will be shown`() = runTest { + val lockScreenStore = InMemoryLockScreenStore() + val pinCodeManager = aPinCodeManager( + lockScreenStore = lockScreenStore, + ) + val presenter = createPinUnlockPresenter( + pinCodeManager = pinCodeManager, + ) + // Delete the pin code from the store + lockScreenStore.deleteEncryptedPinCode() + presenter.test { + skipItems(1) + awaitItem().also { state -> + assertThat(state.pinEntry).isInstanceOf(AsyncData.Failure::class.java) + assertThat(state.showSignOutPrompt).isTrue() + assertThat(state.isSignOutPromptCancellable).isFalse() + assertThat(state.remainingAttempts.dataOrNull()).isEqualTo(3) + } + } + } + private fun AsyncData.assertText(text: String) { dataOrNull()?.assertText(text) } @@ -131,9 +154,10 @@ class PinUnlockPresenterTest { private suspend fun TestScope.createPinUnlockPresenter( biometricAuthenticatorManager: BiometricAuthenticatorManager = FakeBiometricAuthenticatorManager(), callback: PinCodeManager.Callback = DefaultPinCodeManagerCallback(), - logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase(logoutLambda = { "" }), + logoutUseCase: FakeLogoutUseCase = FakeLogoutUseCase(logoutLambda = {}), + pinCodeManager: PinCodeManager = aPinCodeManager() ): PinUnlockPresenter { - val pinCodeManager = aPinCodeManager().apply { + pinCodeManager.apply { addCallback(callback) createPinCode(completePin) } From d18b5292856ea04ab91630281f29440cba1a0633 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 12:08:33 +0200 Subject: [PATCH 114/471] Ensure that remaining pin code attempts is never bigger than the value in the config. --- .../lockscreen/impl/storage/PreferencesLockScreenStore.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt index 98435f962d..bce20b2418 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/storage/PreferencesLockScreenStore.kt @@ -82,5 +82,7 @@ class PreferencesLockScreenStore( } } - private fun Preferences.getRemainingPinCodeAttemptsNumber() = this[remainingAttemptsKey] ?: lockScreenConfig.maxPinCodeAttemptsBeforeLogout + private fun Preferences.getRemainingPinCodeAttemptsNumber() = + this[remainingAttemptsKey]?.coerceIn(0, lockScreenConfig.maxPinCodeAttemptsBeforeLogout) + ?: lockScreenConfig.maxPinCodeAttemptsBeforeLogout } From ae044607c73c2a31905039d111c3c3a782937453 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 May 2026 14:48:55 +0200 Subject: [PATCH 115/471] Add missing screenshot and improve UX when user enter wrong pin then correct pin. --- .../impl/unlock/PinUnlockStateProvider.kt | 13 +++-- .../lockscreen/impl/unlock/PinUnlockView.kt | 52 ++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt index 2beb8babe3..1b8166a8ac 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockStateProvider.kt @@ -20,7 +20,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aPinUnlockState(), - aPinUnlockState(pinEntry = PinEntry.createEmpty(4).fillWith("12")), + aPinUnlockState(pinEntry = AsyncData.Success(PinEntry.createEmpty(4).fillWith("12"))), aPinUnlockState(showWrongPinTitle = true), aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showBiometricUnlock = false), @@ -31,11 +31,18 @@ open class PinUnlockStateProvider : PreviewParameterProvider { BiometricUnlockError(BiometricPrompt.ERROR_LOCKOUT, "Biometric auth disabled") ) ), + aPinUnlockState(showSignOutPrompt = true, pinEntry = AsyncData.Failure(Exception("An error occurred"))), + // User enter wrong pin once, and then correct PIN. In this case, the error (with counter reset to 3) should not be displayed. + aPinUnlockState( + remainingAttempts = AsyncData.Success(2), + showWrongPinTitle = true, + isUnlocked = true, + ), ) } fun aPinUnlockState( - pinEntry: PinEntry = PinEntry.createEmpty(4), + pinEntry: AsyncData = AsyncData.Success(PinEntry.createEmpty(4)), remainingAttempts: AsyncData = AsyncData.Success(3), showWrongPinTitle: Boolean = false, showSignOutPrompt: Boolean = false, @@ -44,7 +51,7 @@ fun aPinUnlockState( isUnlocked: Boolean = false, signOutAction: AsyncAction = AsyncAction.Uninitialized, ) = PinUnlockState( - pinEntry = AsyncData.Success(pinEntry), + pinEntry = pinEntry, showWrongPinTitle = showWrongPinTitle, remainingAttempts = remainingAttempts, showSignOutPrompt = showSignOutPrompt, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 465f015cdd..6749697b64 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -108,10 +108,10 @@ private fun PinUnlockPage( ) { BoxWithConstraints { val commonModifier = Modifier - .fillMaxSize() - .systemBarsPadding() - .imePadding() - .padding(all = 20.dp) + .fillMaxSize() + .systemBarsPadding() + .imePadding() + .padding(all = 20.dp) val header = @Composable { PinUnlockHeader( @@ -147,8 +147,8 @@ private fun PinUnlockPage( state.eventSink(PinUnlockEvent.OnPinEntryChanged(it)) }, modifier = Modifier - .focusRequester(focusRequester) - .fillMaxWidth() + .focusRequester(focusRequester) + .fillMaxWidth() ) } } else { @@ -217,8 +217,8 @@ private fun PinUnlockCompactView( } BoxWithConstraints( modifier = Modifier - .weight(1f) - .fillMaxHeight(), + .weight(1f) + .fillMaxHeight(), contentAlignment = Alignment.Center, ) { content() @@ -239,9 +239,9 @@ private fun PinUnlockExpandedView( header() BoxWithConstraints( modifier = Modifier - .weight(1f) - .fillMaxWidth() - .padding(top = 40.dp), + .weight(1f) + .fillMaxWidth() + .padding(top = 40.dp), ) { content() } @@ -274,8 +274,8 @@ private fun PinDot( } Box( modifier = Modifier - .size(14.dp) - .background(backgroundColor, CircleShape) + .size(14.dp) + .background(backgroundColor, CircleShape) ) } @@ -311,14 +311,26 @@ private fun PinUnlockHeader( ) Spacer(Modifier.height(8.dp)) val remainingAttempts = state.remainingAttempts.dataOrNull() - val subtitle = if (remainingAttempts != null) { - if (state.showWrongPinTitle) { - pluralStringResource(id = R.plurals.screen_app_lock_subtitle_wrong_pin, count = remainingAttempts, remainingAttempts) - } else { - pluralStringResource(id = R.plurals.screen_app_lock_subtitle, count = remainingAttempts, remainingAttempts) + val subtitle = when { + state.isUnlocked -> { + // Hide any previous error + "" } - } else { - "" + remainingAttempts != null -> + if (state.showWrongPinTitle) { + pluralStringResource( + id = R.plurals.screen_app_lock_subtitle_wrong_pin, + count = remainingAttempts, + remainingAttempts, + ) + } else { + pluralStringResource( + id = R.plurals.screen_app_lock_subtitle, + count = remainingAttempts, + remainingAttempts, + ) + } + else -> "" } val subtitleColor = if (state.showWrongPinTitle) { ElementTheme.colors.textCriticalPrimary From 866c8375b305a90119a2bb713a66c32443638e84 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 7 May 2026 11:11:07 +0200 Subject: [PATCH 116/471] Make send event state UI easier to click (#6739) * Make send event state UI easier to click Make it so the whole timestamp view can be clicked * Update screenshots * Simplify `clickableModifier` --------- Co-authored-by: ElementBot --- .../components/TimelineEventTimestampView.kt | 53 +++++++++++++------ ...ts_TimelineEventTimestampView_Day_3_en.png | 2 +- ..._TimelineEventTimestampView_Night_3_en.png | 4 +- ...lineItemEventRowForDirectRoom_Day_0_en.png | 4 +- ...neItemEventRowForDirectRoom_Night_0_en.png | 4 +- ...ts_TimelineItemEventRowShield_Day_0_en.png | 4 +- ..._TimelineItemEventRowShield_Night_0_en.png | 4 +- ...mponents_TimelineItemEventRow_Day_0_en.png | 4 +- ...onents_TimelineItemEventRow_Night_0_en.png | 2 +- ...s.impl.timeline_TimelineView_Day_17_en.png | 4 +- ...es.impl.timeline_TimelineView_Day_2_en.png | 4 +- ...es.impl.timeline_TimelineView_Day_3_en.png | 4 +- ...es.impl.timeline_TimelineView_Day_9_en.png | 4 +- ...impl.timeline_TimelineView_Night_17_en.png | 4 +- ....impl.timeline_TimelineView_Night_2_en.png | 4 +- ....impl.timeline_TimelineView_Night_3_en.png | 4 +- ....impl.timeline_TimelineView_Night_9_en.png | 4 +- ...ures.messages.impl_MessagesViewA11y_en.png | 4 +- 18 files changed, 68 insertions(+), 49 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 21ef7c5b09..25a53cec2d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -15,9 +15,12 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -47,9 +50,39 @@ fun TimelineEventTimestampView( val isMessageEdited = event.content.isEdited() val isMessageRedacted = event.content.isRedacted() val tint = if (hasError || hasEncryptionCritical && !isMessageRedacted) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary + + val shield = event.messageShield + val isVerifiedUserSendFailure = event.localSendState is LocalEventSendState.Failed.VerifiedUser + val onClickLabel = when { + shield != null -> stringResource(CommonStrings.a11y_view_details) + hasError && isVerifiedUserSendFailure -> stringResource(CommonStrings.action_open_context_menu) + else -> null + } + val clickableModifier = remember(shield, hasError) { + when { + shield != null -> { + Modifier.clickable( + onClickLabel = onClickLabel, + ) { + eventSink(TimelineEvent.ShowShieldDialog(shield)) + } + } + hasError -> Modifier + .clickable( + enabled = isVerifiedUserSendFailure, + onClickLabel = onClickLabel, + ) { + eventSink(TimelineEvent.ComputeVerifiedUserSendFailure(event)) + } + else -> Modifier + } + } Row( modifier = Modifier .padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)) + // For a better click target, make the corners rounded + .clip(RoundedCornerShape(8.dp)) + .then(clickableModifier) .then(modifier), verticalAlignment = Alignment.CenterVertically, ) { @@ -67,36 +100,22 @@ fun TimelineEventTimestampView( color = tint, ) if (hasError) { - val isVerifiedUserSendFailure = event.localSendState is LocalEventSendState.Failed.VerifiedUser Spacer(modifier = Modifier.width(2.dp)) Icon( imageVector = CompoundIcons.ErrorSolid(), contentDescription = stringResource(id = CommonStrings.common_sending_failed), tint = tint, - modifier = Modifier - .size(15.dp, 18.dp) - .clickable( - enabled = isVerifiedUserSendFailure, - onClickLabel = stringResource(CommonStrings.action_open_context_menu), - ) { - eventSink(TimelineEvent.ComputeVerifiedUserSendFailure(event)) - } + modifier = Modifier.size(15.dp, 18.dp), ) } if (!isMessageRedacted) { - event.messageShield?.let { shield -> + shield?.let { shield -> Spacer(modifier = Modifier.width(2.dp)) Icon( imageVector = shield.toIcon(), contentDescription = stringResource(id = CommonStrings.a11y_encryption_details), - modifier = Modifier - .size(15.dp) - .clickable( - onClickLabel = stringResource(CommonStrings.a11y_view_details), - ) { - eventSink(TimelineEvent.ShowShieldDialog(shield)) - }, + modifier = Modifier.size(15.dp), tint = shield.toIconColor(), ) Spacer(modifier = Modifier.width(4.dp)) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en.png index 2faf0a5bb2..e1f1510b5d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ef807d846ee06add8438e81ece8570cf688742268af87b6985551c3cb2a0faf +oid sha256:4bb4d59085cc26a284e1bd9c2e6fb7e982a1b32eae5f46282a4dde93b5d5ba1f size 5735 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en.png index fdbb4a4aae..b09b378502 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46b2bf1b4ed49f02ab2ff5218ad6bf2a7e0184e022af60d4cbeb8599646975c8 -size 5670 +oid sha256:e6cf51de97f040b1028bc32d167f7c4acdbd2717c3222e1a1d97929d666cacc0 +size 5669 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en.png index 592d4b0e0d..a0b392a659 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87b78e2a06bf592ac538eecfdca70a9c5ec51f69e397623edcdcad8daaa9bfe8 -size 293405 +oid sha256:495951c4763e1c3015a88964683d384b84dbfce1b9569630d81ff8578a7d13e2 +size 293410 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en.png index a84752536c..97826e6a68 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bccf822697a9710752ce5323c8b6481d7080d9eba7579d9679f451818ab71a6 -size 293202 +oid sha256:25d4838bf2587c2ccfffe83d53d556ec35ee7691e6eff91689d41dfcd538a324 +size 293209 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en.png index 4faa1f828a..90e9e72d8f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6982eb3368779c8e7fa602059d9af4119f450f9f2f7f384b0e1251080d286c1 -size 377103 +oid sha256:7684bb50d0012a7175957d0bbac28f68856573cea37f45697ef3b81235f1eafb +size 377102 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en.png index 14d9ae5907..c7a8f44df4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:add275ce7641fbeba7acca740b2d0d6df0ed06c1c3c5cbcd05a1ca474b086ccf -size 375878 +oid sha256:f6677e0cda28bab1973cc767606eedf86eae3a1ae111b552c6f284eae4fb2de2 +size 375875 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en.png index 480ae88643..cacc88a34c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2476a6e3d5beff4360a349adb831e63fbc05962b57bb0f551ec784e28b9495d1 -size 411656 +oid sha256:789dbfa4886cacdaf5bab7bd6e501d0dd6e31af965b398d9f61d7e46c653a8c9 +size 411661 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en.png index 75cdc28a3e..e85c9987f7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cb2e25a8f461fbb9ca9082bf94571daae1d2c886d2cd33ecea14f955eb959b2 +oid sha256:a740b9d1af8d48d5b9f9bd47abab34ed37037c7c2646d92a05af139435f23d27 size 409955 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 72f4fa03b5..39bbd406ed 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:4a0827eb23b48bd03fde5078598cb7827459a213ff0a693890581628330f6939 -size 66845 +oid sha256:6f7e3f1cdaf18d7d529be07cde3bdc9100243780101e73dd53c3d78639775974 +size 66862 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 67aca77a8c..414db592da 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:c94c770cbd1f41f0b50d0a64290c96e1a75bdcddbd0f850d097cd41e5f2a9cf5 -size 493537 +oid sha256:467e5aee42e9041db2672dc0d37e98fc1090c19207604a31247f3142d845f792 +size 493534 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 dde5ade114..b9723dd5c4 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:9d73c338dce5b27c718cffbbf7cb1fe8f5b9d72f0d335afd81526849190d4941 -size 488704 +oid sha256:723238ad84c9925eb0edbbf225b50a03bf29e5671777d9f03eba6b910ee00fcf +size 488705 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 adf8d255e3..6ff4840c57 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:a8fa43d296a984d242ed56f7879de4c38f3da94147c365c424248eba2c4ad61d -size 371263 +oid sha256:fbd5652457d495c3c6692c151be27c985cf47e95c8b89af9b6edc8c1b734acc8 +size 371265 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 3ad7246822..e5795401ce 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:bea070091132156a809da1c185e2f94333e62f8ea9ae7d6d1b789adffb869522 -size 55538 +oid sha256:06e2c25d3cbd111b22bd0188e2b1880729e023170c7bed972cdb3c34544ba55e +size 55547 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 fa93db2f0d..c418921834 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:c57dbc66342263c4ff1d47c18099dd2cc22b752f1b6bfc853566706dc0cf5110 -size 486435 +oid sha256:3bf788838a204acb53bceba268327e3c2a772447490a597ed4929487716bc6fb +size 486425 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 150b2b0c87..6ab414ab0b 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:5d4fff1585f602d561a98bfbd907d3c7499e6717f488eee711e90f2b0f5df7b9 -size 481595 +oid sha256:80e198bd8cf523c785de4a66c4c22cecb22d1040377bdfd66eeed821a4281592 +size 481590 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 7b5512dcc7..825618ae17 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:bcd9e0934b5f6808d58a3d3506641f173c4b223d78c605de09832d3741e8834f -size 149300 +oid sha256:75ace43e27a0c8f9786b0494931c3ef5e12d903e7417dddecfb34c9526e74f3c +size 149314 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png index c214011ea7..631559cabc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23aabf96e5dc84257968a77ad406a8f3952b24fa8d8c03612bddfc08b6c990c6 -size 131751 +oid sha256:ee269e52306e048bbd0a06ea7c56f9080f37f37b00909c3d48a02a3a37bd57bc +size 131800 From 790c30ca1f210c9d38f4deb8cf1072da6a6f38a8 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 7 May 2026 09:20:09 +0000 Subject: [PATCH 117/471] Update screenshots --- ...ures.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en.png | 3 +++ ...ures.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en.png | 3 +++ ...es.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en.png | 3 +++ ...es.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en.png | 3 +++ .../features.lockscreen.impl.unlock_PinUnlockView_Day_8_en.png | 3 +++ .../features.lockscreen.impl.unlock_PinUnlockView_Day_9_en.png | 3 +++ ...eatures.lockscreen.impl.unlock_PinUnlockView_Night_8_en.png | 3 +++ ...eatures.lockscreen.impl.unlock_PinUnlockView_Night_9_en.png | 3 +++ 8 files changed, 24 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en.png new file mode 100644 index 0000000000..5669a59d24 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8665179304ccd2e0acf517ddcf369c4daae51c41732ee1b9618e3bedf38aeffc +size 31643 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en.png new file mode 100644 index 0000000000..265044a0b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14300f3af0bc8c003ce94868665547ad439826b47cf416f1a062ffa4a1d7e793 +size 15782 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en.png new file mode 100644 index 0000000000..ff95140047 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88354d222679723cadfb76d2c4928fad78e17fec13514c0e4712e6f877b1a0c7 +size 29627 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en.png new file mode 100644 index 0000000000..4bc9f6653b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d505c4b2323dfc67c1b7e119960ede5b97b52d6c453610cdbb01efd485730aa5 +size 15369 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_8_en.png new file mode 100644 index 0000000000..8b7fa8b77c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80246e3957a0e3f98bce55ddd6d8fe09b5f140eee3837d80ca20253f6bcfb876 +size 37738 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_9_en.png new file mode 100644 index 0000000000..266dae3c81 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60dd90eb571f473b78ecc629573bcf2faca02819022bdf57f05cc8b0876f4ab8 +size 31440 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_8_en.png new file mode 100644 index 0000000000..8a8d15acf9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d60a8caa2441ff0618636d307d32d0944bdb77fb3b579c0959dc951b0318ad72 +size 35429 diff --git a/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_9_en.png new file mode 100644 index 0000000000..fe27573765 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.lockscreen.impl.unlock_PinUnlockView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfe8b59b8d5d7a1c33285072dec437060faae55bfa2a42687bd8895dd90409a6 +size 30310 From 80a46fdb3dabbcf57a667627a24d81bd9a16efdc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 May 2026 11:52:21 +0200 Subject: [PATCH 118/471] Fix test --- .../invitepeople/impl/DefaultInvitePeoplePresenterTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index e7fef11423..4ecd74a42f 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -320,7 +320,7 @@ internal class DefaultInvitePeoplePresenterTest { val initialState = awaitItemAsDefault() skipItems(1) - val selectedUser = aMatrixUser() + val selectedUser = aMatrixUser(displayName = "John Doe") initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(selectedUser)) @@ -358,7 +358,7 @@ internal class DefaultInvitePeoplePresenterTest { val initialState = awaitItemAsDefault() skipItems(1) - val selectedUser = aMatrixUser() + val selectedUser = aMatrixUser(displayName = "John Doe") // Given a query is made initialState.searchQuery.setTextAndPlaceCursorAtEnd("some query") From 022d21b097630aad8c8249222bf6b579a1fc1a56 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 May 2026 11:54:27 +0200 Subject: [PATCH 119/471] Remove trailing spaces --- .../impl/developer/appsettings/AppDeveloperSettingsView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt index f60eb2ac85..9b2a2aa3a9 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt @@ -87,7 +87,6 @@ fun AppDeveloperSettingsView( onClick = onOpenShowkase ) } - RageshakePreferencesView( state = state.rageshakeState, ) From a6bee757b4e936fd2403db2d2412e3415ecd6f97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 12:11:36 +0000 Subject: [PATCH 120/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.7 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 581630c80d..4a1e0c2f80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.6" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.7" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From 7e3acc03b9c6db92ee67da231b553166313424d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 7 May 2026 15:13:51 +0200 Subject: [PATCH 121/471] Fix API breaks --- .../matrix/impl/fixtures/factories/NotificationItem.kt | 2 ++ .../libraries/matrix/impl/fixtures/factories/SpaceRoom.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt index 82984c480d..63ad77238b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/NotificationItem.kt @@ -67,6 +67,7 @@ internal fun aRustNotificationRoomInfo( joinedMembersCount: ULong = 2u, isEncrypted: Boolean? = true, isDirect: Boolean = false, + isDm: Boolean = false, joinRule: JoinRule? = null, isSpace: Boolean = false, serviceMembers: List = emptyList(), @@ -79,6 +80,7 @@ internal fun aRustNotificationRoomInfo( joinedMembersCount = joinedMembersCount, isEncrypted = isEncrypted, isDirect = isDirect, + isDm = isDm, joinRule = joinRule, isSpace = isSpace, serviceMembers = serviceMembers.map { it.value }, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt index 50115055c2..50e2f6168e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt @@ -19,6 +19,7 @@ import org.matrix.rustcomponents.sdk.SpaceRoom internal fun aRustSpaceRoom( roomId: RoomId = A_ROOM_ID, isDirect: Boolean = false, + isDm: Boolean = false, canonicalAlias: String? = null, rawName: String? = null, displayName: String = "", @@ -35,6 +36,7 @@ internal fun aRustSpaceRoom( ) = SpaceRoom( roomId = roomId.value, isDirect = isDirect, + isDm = isDm, canonicalAlias = canonicalAlias, rawName = rawName, displayName = displayName, From 95dac66869dd350c84f80d3c4af61caef75500d8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 15:39:26 +0200 Subject: [PATCH 122/471] Update tspascoal/get-user-teams-membership action to v4 (#6747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tspascoal/get-user-teams-membership action to v4 * Point the commit to the right tag v4.0.0 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index c8c0a98dea..9b4a5e3689 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Check membership if: github.event.pull_request.user.login != 'renovate[bot]' - uses: tspascoal/get-user-teams-membership@57e9f42acd78f4d0f496b3be4368fc5f62696662 # v3 + uses: tspascoal/get-user-teams-membership@b1480b119326dde04ceffbeccd98e41892539c74 # v4.0.0 id: teams with: username: ${{ github.event.pull_request.user.login }} From 0cb1a9fb145ee1879f923ffb50644944584917bc Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 May 2026 16:01:34 +0200 Subject: [PATCH 123/471] Setting version for the release 26.05.0 --- plugins/src/main/kotlin/Versions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index d6a2cbea3d..fa197e4a08 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -39,13 +39,13 @@ private const val versionYear = 26 * Month of the version on 2 digits. Value must be in [1,12]. * Do not update this value. it is updated by the release script. */ -private const val versionMonth = 4 +private const val versionMonth = 5 /** * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 4 +private const val versionReleaseNumber = 0 object Versions { /** From 63edcd69898a7adc4b2008365e718d48fc11a7b2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 May 2026 16:01:40 +0200 Subject: [PATCH 124/471] Adding fastlane file for version 26.05.0 --- fastlane/metadata/android/en-US/changelogs/202605000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202605000.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202605000.txt b/fastlane/metadata/android/en-US/changelogs/202605000.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202605000.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 245c30c18a9ba54dabd6df86a07138ff2cb11bd4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 7 May 2026 17:47:15 +0200 Subject: [PATCH 125/471] Changelog for version 26.05.0 --- CHANGES.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index df109d34a6..a57395b833 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,88 @@ +Changes in Element X v26.05.0 +============================= + + + +## What's Changed +### ✨ Features +* Add flag for automatic back pagination feature by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6637 +* Promote "history sharing on invite" out of developer options by @richvdh in https://github.com/element-hq/element-x-android/pull/6647 +* Remove RoomDirectorySearch feature flag — always enable the feature by @Copilot in https://github.com/element-hq/element-x-android/pull/6736 +### 🙌 Improvements +* Change native back button behavior in EC view (close settings in EC with os native back) by @toger5 in https://github.com/element-hq/element-x-android/pull/6642 +* Revert PR #6642 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6724 +* Use 'Report a problem' string instead of 'Report bug' by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6735 +### 🐛 Bugfixes +* Remove distributed tracing of the 'timeline loading' flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6644 +* Set max lines for 'in reply to' view conditionally by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6612 +* Mention pill cut off by @bmarty in https://github.com/element-hq/element-x-android/pull/6651 +* Ensure that bottom sheet can scroll by @bmarty in https://github.com/element-hq/element-x-android/pull/6661 +* Remove legacy `mx-reply` from `toPlainText` formatted event contents by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6683 +* Fix ANRs when receiving push notifications by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6696 +* Mitigate a deadlock when loading room timelines by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6674 +* Fix calls on Huawei devices: skip addWebMessageListener on Chromium < 119 by @manfrommedan in https://github.com/element-hq/element-x-android/pull/6640 +* Allow cancelling room loading in Home screen by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6723 +* Let our Json parser accept comments and trailing comma. by @bmarty in https://github.com/element-hq/element-x-android/pull/6700 +* Fix low width image message by @krbns in https://github.com/element-hq/element-x-android/pull/6692 +* Make icons in the Chat screen top bar 16dp by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6733 +* Fix back button sometimes not working after exiting a thread by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6732 +* Make send event state UI easier to click by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6739 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6658 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6716 +### 🧱 Build +* Fix record screenshots action permissions by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6679 +* Fix dependency error by @bmarty in https://github.com/element-hq/element-x-android/pull/6697 +### 🚧 In development 🚧 +* [Link new device] Add missing screen to render digits that the user has to type on the other device by @bmarty in https://github.com/element-hq/element-x-android/pull/6680 +### Dependency upgrades +* Update dependency io.nlopez.compose.rules:detekt to v0.5.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6594 +* Update zizmorcore/zizmor-action action to v0.5.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6630 +* Update dependency io.sentry:sentry-android to v8.38.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6597 +* fix(deps): update camera to v1.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6514 +* Update dependency io.sentry:sentry-android to v8.39.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6648 +* Update dependency io.element.android:element-call-embedded to v0.19.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6662 +* Update dependencyAnalysis to v3.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6657 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.04.27 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6666 +* Update dependency io.sentry:sentry-android to v8.40.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6691 +* Update dependency org.jsoup:jsoup to v1.22.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6660 +* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6687 +* Update dependency androidx.compose:compose-bom to v2026.04.01 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6693 +* Update dependency io.nlopez.compose.rules:detekt to v0.5.8 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6711 +* Update dependency com.posthog:posthog-android to v3.43.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6704 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6718 +* Update roborazzi to v1.60.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6722 +* Update dependency net.zetetic:sqlcipher-android to v4.15.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6727 +* Update dependency org.maplibre.gl:android-sdk to v13.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6731 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6734 +* Update dependencyAnalysis to v3.10.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6742 +* Update tspascoal/get-user-teams-membership action to v4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6747 +### Others +* devx: fix build sdk script options for macos by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6636 +* PR:Fix mention pill cut off by @krbns in https://github.com/element-hq/element-x-android/pull/6622 +* Update media viewer UI by @bmarty in https://github.com/element-hq/element-x-android/pull/6643 +* Strip formatting from media captions in room summary by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6670 +* Update error mappings for Link new device flow by @hughns in https://github.com/element-hq/element-x-android/pull/6677 +* Rename `OIDC` components and variables to `OAuth` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6686 +* [Link new device] Add missing error case "already signed in" by @bmarty in https://github.com/element-hq/element-x-android/pull/6688 +* Improve detection of completion for Link new device flow by @hughns in https://github.com/element-hq/element-x-android/pull/6681 +* Remove external call support by @bmarty in https://github.com/element-hq/element-x-android/pull/6668 +* [a11y] Fix a set of issues by @bmarty in https://github.com/element-hq/element-x-android/pull/6650 +* Add clipping to RoomSummaryRow by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6654 +* Fix media viewer flickering and crashing by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6715 +* Rename verification methods by @bmarty in https://github.com/element-hq/element-x-android/pull/6726 +* Add a way to tweak MAS url. by @bmarty in https://github.com/element-hq/element-x-android/pull/6682 +* Fix 2 x Crash the app in Developer Options - Update AppDeveloperSettingsView.kt by @escix in https://github.com/element-hq/element-x-android/pull/6708 +* Introduce UI sample by @bmarty in https://github.com/element-hq/element-x-android/pull/6740 + +## New Contributors +* @krbns made their first contribution in https://github.com/element-hq/element-x-android/pull/6622 +* @toger5 made their first contribution in https://github.com/element-hq/element-x-android/pull/6642 +* @manfrommedan made their first contribution in https://github.com/element-hq/element-x-android/pull/6640 +* @Copilot made their first contribution in https://github.com/element-hq/element-x-android/pull/6736 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.04.4...v26.05.0 + Changes in Element X v26.04.4 ============================= From 4a4b3e07ef1cc3989809fc9f2b1da15a7ec9a577 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Fri, 8 May 2026 11:17:30 +0200 Subject: [PATCH 126/471] Use just the other user's avatar for DM details (#6738) * Use just the other user's avatar for DM details. Remove `DmAvatars` component and other no longer needed data. * Improve selection indicator by clipping the avatar to a circle shape * Update screenshots --------- Co-authored-by: ElementBot --- .../roomdetails/impl/RoomDetailsPresenter.kt | 16 +- .../roomdetails/impl/RoomDetailsState.kt | 5 +- .../impl/RoomDetailsStateProvider.kt | 6 +- .../roomdetails/impl/RoomDetailsView.kt | 32 +++- .../impl/RoomDetailsPresenterTest.kt | 7 +- .../roomdetails/impl/RoomDetailsViewTest.kt | 12 +- .../components/avatar/DmAvatars.kt | 142 ------------------ ...es.roomdetails.impl_RoomDetailsA11y_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_0_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_10_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_11_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_12_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_13_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_14_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_15_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_16_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_17_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_18_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_19_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_1_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_20_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_21_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_22_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_2_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_3_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_4_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_5_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_6_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_7_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_8_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_9_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_0_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_10_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_11_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_12_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_13_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_14_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_15_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_16_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_17_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_18_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_19_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_1_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_20_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_21_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_22_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_2_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_3_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_4_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_5_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_6_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_7_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_8_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_9_en.png | 4 +- ...ponents.avatar_DmAvatarsRtl_Avatars_en.png | 3 - ...components.avatar_DmAvatars_Avatars_en.png | 3 - 56 files changed, 128 insertions(+), 286 deletions(-) delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatars_Avatars_en.png 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 5fd44076e6..2ba7b4fa4a 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 @@ -47,7 +47,6 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomNotificationSettings -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.roomMemberIdentityStateChange import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -99,9 +98,8 @@ class RoomDetailsPresenter( val canonicalAlias by remember { derivedStateOf { roomInfo.canonicalAlias } } val isEncrypted by remember { derivedStateOf { roomInfo.isEncrypted == true } } val dmMember by room.getDirectRoomMember(membersState) - val currentMember by room.getCurrentRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) - val roomType = getRoomType(dmMember, currentMember) + val roomType = getRoomType(dmMember) val roomCallState = roomCallStatePresenter.present() val joinedMemberCount by remember { derivedStateOf { roomInfo.joinedMembersCount } } @@ -210,15 +208,9 @@ class RoomDetailsPresenter( } @Composable - private fun getRoomType( - dmMember: RoomMember?, - currentMember: RoomMember?, - ): RoomDetailsType = remember(dmMember, currentMember) { - if (dmMember != null && currentMember != null) { - RoomDetailsType.Dm( - me = currentMember, - otherMember = dmMember, - ) + private fun getRoomType(dmMember: RoomMember?): RoomDetailsType = remember(dmMember) { + if (dmMember != null) { + RoomDetailsType.Dm(otherMember = dmMember) } else { RoomDetailsType.Room } 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 e74f71322d..90a912e86f 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 @@ -77,10 +77,7 @@ data class RoomDetailsState( @Immutable sealed interface RoomDetailsType { data object Room : RoomDetailsType - data class Dm( - val me: RoomMember, - val otherMember: RoomMember, - ) : RoomDetailsType + data class Dm(val otherMember: RoomMember) : RoomDetailsType } @Immutable 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 7f3701a770..a8cd84be72 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 @@ -13,7 +13,6 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState 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.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.aUserProfileState @@ -179,10 +178,7 @@ fun aDmRoomDetailsState( roomName = roomName, isPublic = false, isEncrypted = isEncrypted, - roomType = RoomDetailsType.Dm( - me = aRoomMember(), - otherMember = aDmRoomMember(isIgnored = isDmMemberIgnored), - ), + roomType = RoomDetailsType.Dm(otherMember = aDmRoomMember(isIgnored = isDmMemberIgnored)), roomMemberDetailsState = aUserProfileState( isBlocked = AsyncData.Success(isDmMemberIgnored), verificationState = dmRoomMemberVerificationState, 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 c48716db11..e877c24554 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 @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -31,6 +32,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -52,7 +54,6 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType -import io.element.android.libraries.designsystem.components.avatar.DmAvatars import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.MainActionButton import io.element.android.libraries.designsystem.components.list.ListItemContent @@ -91,6 +92,7 @@ import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.compose.LocalAnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @Composable @@ -154,9 +156,9 @@ fun RoomDetailsView( } is RoomDetailsType.Dm -> { DmHeaderSection( - me = state.roomType.me, otherMember = state.roomType.otherMember, roomName = state.roomName, + isTombstoned = state.isTombstoned, openAvatarPreview = { name, avatarUrl -> openAvatarPreview(name, avatarUrl) }, @@ -417,6 +419,7 @@ private fun RoomHeaderSection( ), contentDescription = stringResource(CommonStrings.a11y_room_avatar), modifier = Modifier + .clip(CircleShape) .clickable( enabled = avatarUrl != null, onClickLabel = stringResource(CommonStrings.action_view), @@ -435,9 +438,9 @@ private fun RoomHeaderSection( @Composable private fun DmHeaderSection( - me: RoomMember, otherMember: RoomMember, roomName: String, + isTombstoned: Boolean, openAvatarPreview: (name: String, url: String) -> Unit, onSubtitleClick: (String) -> Unit, modifier: Modifier = Modifier @@ -448,11 +451,24 @@ private fun DmHeaderSection( .padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - DmAvatars( - userAvatarData = me.getAvatarData(size = AvatarSize.DmCluster), - otherUserAvatarData = otherMember.getAvatarData(size = AvatarSize.DmCluster), - openAvatarPreview = { url -> openAvatarPreview(me.getBestName(), url) }, - openOtherAvatarPreview = { url -> openAvatarPreview(roomName, url) }, + Avatar( + avatarData = AvatarData(otherMember.userId.value, roomName, otherMember.avatarUrl, AvatarSize.RoomDetailsHeader), + avatarType = AvatarType.Room( + heroes = persistentListOf( + otherMember.getAvatarData(size = AvatarSize.RoomDetailsHeader) + ), + isTombstoned = isTombstoned, + ), + contentDescription = stringResource(CommonStrings.a11y_room_avatar), + modifier = Modifier + .clip(CircleShape) + .clickable( + enabled = otherMember.avatarUrl != null, + onClickLabel = stringResource(CommonStrings.action_view), + ) { + openAvatarPreview(otherMember.getBestName(), otherMember.avatarUrl!!) + } + .testTag(TestTags.roomDetailAvatar) ) TitleAndSubtitle( title = roomName, 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 d355010307..1b93a92884 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 @@ -206,12 +206,7 @@ class RoomDetailsPresenterTest { val presenter = createRoomDetailsPresenter(room) presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { val initialState = awaitItem() - assertThat(initialState.roomType).isEqualTo( - RoomDetailsType.Dm( - me = myRoomMember, - otherMember = otherRoomMember, - ) - ) + assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherMember = otherRoomMember)) cancelAndIgnoreRemainingEvents() } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 50139e0149..5fe0130c73 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -126,10 +126,7 @@ class RoomDetailsViewTest { state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), canInvite = true, - roomType = RoomDetailsType.Dm( - aRoomMember(UserId("@me:local.org")), - aRoomMember(UserId("@other:local.org")) - ), + roomType = RoomDetailsType.Dm(aRoomMember(UserId("@other:local.org"))), ), onJoinCallClick = callback, ) @@ -232,10 +229,7 @@ class RoomDetailsViewTest { fun `click on avatar test on DM`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aRoomDetailsState( - roomType = RoomDetailsType.Dm( - aRoomMember(), - aDmRoomMember(avatarUrl = "an_avatar_url"), - ), + roomType = RoomDetailsType.Dm(aDmRoomMember(avatarUrl = "an_avatar_url"),), roomName = "Daniel", eventSink = eventsRecorder, ) @@ -244,7 +238,7 @@ class RoomDetailsViewTest { state = state, openAvatarPreview = callback, ) - onNodeWithTag(TestTags.memberDetailAvatar.value).performClick() + onNodeWithTag(TestTags.roomDetailAvatar.value).performClick() callback.assertSuccess() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt deleted file mode 100644 index f19796e053..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.designsystem.components.avatar - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.CompositingStrategy -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.LayoutDirection -import io.element.android.libraries.designsystem.preview.ElementThemedPreview -import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE -import io.element.android.libraries.designsystem.preview.USER_NAME_BOB -import io.element.android.libraries.designsystem.text.toPx -import io.element.android.libraries.testtags.TestTags -import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.ui.strings.CommonStrings - -/** Ratio between the box size (120 on Figma) and the avatar size (75 on Figma). */ -private const val SIZE_RATIO = 1.6f - -/** - * https://www.figma.com/design/A2pAEvTEpJZBiOPUlcMnKi/Settings-%2B-Room-Details-(new)?node-id=1787-56333 - */ -@Composable -fun DmAvatars( - userAvatarData: AvatarData, - otherUserAvatarData: AvatarData, - openAvatarPreview: (url: String) -> Unit, - openOtherAvatarPreview: (url: String) -> Unit, - modifier: Modifier = Modifier, -) { - val boxSize = userAvatarData.size.dp * SIZE_RATIO - val boxSizePx = boxSize.toPx() - val otherAvatarRadius = otherUserAvatarData.size.dp.toPx() / 2 - val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl - Box( - modifier = modifier.size(boxSize), - ) { - // Draw user avatar and cut top end corner - Avatar( - avatarData = userAvatarData, - avatarType = AvatarType.User, - contentDescription = stringResource(CommonStrings.a11y_your_avatar), - modifier = Modifier - .align(Alignment.BottomStart) - .graphicsLayer { - compositingStrategy = CompositingStrategy.Offscreen - } - .drawWithContent { - drawContent() - val xOffset = if (isRtl) { - size.width - boxSizePx + otherAvatarRadius - } else { - boxSizePx - otherAvatarRadius - } - drawCircle( - color = Color.Black, - center = Offset( - x = xOffset, - y = size.height - (boxSizePx - otherAvatarRadius), - ), - radius = otherAvatarRadius / 0.9f, - blendMode = BlendMode.Clear, - ) - } - .clip(CircleShape) - .clickable( - enabled = userAvatarData.url != null, - onClickLabel = stringResource(CommonStrings.action_view), - ) { - userAvatarData.url?.let { openAvatarPreview(it) } - } - ) - // Draw other user avatar - Avatar( - avatarData = otherUserAvatarData, - avatarType = AvatarType.User, - contentDescription = stringResource(CommonStrings.a11y_other_user_avatar), - modifier = Modifier - .align(Alignment.TopEnd) - .clip(CircleShape) - .clickable( - enabled = otherUserAvatarData.url != null, - onClickLabel = stringResource(CommonStrings.action_view), - ) { - otherUserAvatarData.url?.let { openOtherAvatarPreview(it) } - } - .testTag(TestTags.memberDetailAvatar) - ) - } -} - -@Preview(group = PreviewGroup.Avatars) -@Composable -internal fun DmAvatarsPreview() = ElementThemedPreview { - val size = AvatarSize.DmCluster - DmAvatars( - userAvatarData = anAvatarData( - id = "Alice", - name = USER_NAME_ALICE, - size = size, - ), - otherUserAvatarData = anAvatarData( - id = "Bob", - name = USER_NAME_BOB, - size = size, - ), - openAvatarPreview = {}, - openOtherAvatarPreview = {}, - ) -} - -@Preview(group = PreviewGroup.Avatars) -@Composable -internal fun DmAvatarsRtlPreview() { - CompositionLocalProvider( - LocalLayoutDirection provides LayoutDirection.Rtl, - ) { - DmAvatarsPreview() - } -} diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png index 5f0cdd8eca..43a49f7320 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33d583fac967f383a3d3535c4ac38aaccdcbf4a1d48323ff375a239dbce81838 -size 83494 +oid sha256:34ce2f0451fb1681648c91771316256e67b1f295f4deb97f6115ae593d82f2ef +size 83472 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png index d674b1ad00..bb68ee4d65 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ad7b9331a5d504b8d2145c4428e6b64ed838b371f1aad5c288e5545f8a20f1f -size 45464 +oid sha256:eeb2355a693a7ac522028833fea4f0506e1b455e4aee888ed314c2700a9a87af +size 45454 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png index 2567512ca9..77bc91df06 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d74ede198f221b1138ed49bac7e02ee0fefbccd38735b09237fda95a78f6bd2 -size 44174 +oid sha256:b45d35b68d232fc5cbaf3b01156127a77618bcf2197375ad42f61bc7ad8ec400 +size 44161 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png index ee9fcd2088..9f23841e1d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3f483a9e05278928f806f9534720c121efa77bd11b93ea6afc8b543f51a6d7c -size 43171 +oid sha256:6a7efaa997737bfc6db898987b78077ec3c8039a6d0cce84d770bcabcbdc815e +size 43161 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png index 724a99374d..308c764795 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3045102b9695ce3c095ac81e3d3d26dbf182d433427903e280189613a74f2984 -size 44758 +oid sha256:a8de9a9297383ff1ec552304a49040c44236654dd16ba9ae5f54fddf3e14c617 +size 44764 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png index c62a4e99b7..13403ea92d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bbbf23895558745ebe4fab7faee8ffb4acba07f48b652cc130c78ddf89bebef -size 44684 +oid sha256:ff4c856028887c1390df7b313462e37e0723ff98b9f92aa769b2fc9133ab6ec5 +size 44673 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png index f23e6e5532..59ac3c53af 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f72fb044fb52f226600ad2eb3344e8122edcce15c75800fb483f3b93a1c6e26 -size 45092 +oid sha256:162a9cd8b10b8ec7c33dc926c63d2d04986ca0e29a6cd036771ffc137176e28c +size 45079 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png index 47f40ccaab..9d934ba726 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14261c716c286b17ee2afcb652378f9703b39f67b219256f6ac3cde61cd50947 -size 45589 +oid sha256:fcd3e86c18e34e96a2b535340a27a8cb248d48697ce75d1c7c62cde27b3e2ccb +size 45577 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png index fd5b8fce76..cfd42f8af2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1b1c3f8db4dc58dcaab91f254da2906138c09c65d4e57a77a5be9af371aaa5d -size 44944 +oid sha256:d5a20887fa9a6dd7c96f328f24608e1fcea0326b60cf8a7b43e2387fa19595f5 +size 44932 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png index 5491a33e2a..b45fd6c6f1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5d3a4bd5c340c0d439ccd7d7e24f372d89255a775a9497e5d9b8847354cafb6 -size 44236 +oid sha256:8087a634dd7bf3893697d8ab599f9694169e878b4484b0d8e9821307a8423d63 +size 44243 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png index e44f676f01..ddcd78552d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d9a464a1bc24dc76d1a10ae835fb24839b521ba446a1adad3e70f4ae5f17857 -size 42076 +oid sha256:f7b9375965be172b7a1a6b00be38c66dc5f73d7d76f6b07fd1cb8defbadae840 +size 41554 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png index 46bb9b0a89..669af01562 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29592dcede3af29136c857b03975c2697470682371ac5d606f2e6974f89ea268 -size 42030 +oid sha256:7890cc2fc8e722bfdee5e1c0cdda335c386a2e70a918b710a44a788457dd8497 +size 41507 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png index d58516f709..6b89622e9d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:676908cf3eabc3417f228dabd2663bb0aa3d903d1b6f0e2fe770bdd6fbb48af2 -size 40407 +oid sha256:e7b4d5f81f605a0858994e50d72fbd17d2f82e0b2e4673ff9baf6e25a103ef55 +size 40392 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png index 7cc47cba6d..b0a44326fd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c66ebe3c16933495fe33596036363f95e20654b6ef5ee822f594a34531ea640f -size 45211 +oid sha256:fef854b4c908e206ece79491c655fbf0fd5b51883f3d77045c579664331b9058 +size 45195 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png index 8d4880b59f..637549dfc7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de5ccdad3e08ed4fd4330697ad6c7085d5553cb889b7b7c12879aaaa6b34c1de -size 44953 +oid sha256:dde778a13f36958fefba950fe0d3470b7277d22fe4eac189c77b0838b3728ef3 +size 44940 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png index 52e66c38c8..cafc853f58 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e215296d864d34ceacaa53e7fec452643b63d72b253074eb50617bab50915ad1 -size 44670 +oid sha256:d5870cd12f6de78ecf72a9dc8e3716e0e3476ecf2f29647fc1eca900f5e030f9 +size 44652 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png index 2a765938ad..12efcf7526 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae13e58b01e0e43591c0374474b33fea60574a5d8b22cb57092d15637dc58baa -size 37920 +oid sha256:1281394671737e78232cb7d9785b57b8ef2ca50c125f0c0854d101d27efaa939 +size 37910 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png index 8a7f2e2d75..3d6f26c63c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0cc874a4ba2b8bd0f0b7e290d0ffc67e82d1222fd287360c3321143d5f87ad4 -size 42369 +oid sha256:ec2b5711ea9ffacc6aad4f7b16efdcc78603e9eaa9d33abfaa0d867db5aead67 +size 42355 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png index c8d3e8f6fa..90dd2364d3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5bb345d25a8b90a34708123fc6ba72dd6aca2c09ade4c26b29e9168d65e18be -size 42466 +oid sha256:4adfa037ff619098067d03f252ba0af1c3c89553ea0c81dcda41f7f0b655baad +size 42450 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png index cdb5f92f18..31daf341c2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1d4af8779d9869938b17f26b155c584c10d119479c62918d5b819a0db30acac -size 41720 +oid sha256:105399c3ca12de9e911ccf7b4aaebe87bffeba45b1740d067b5b8e67c5647952 +size 41196 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png index 1e0b3070ee..3309d53220 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fac98b00a99771b186e8146b98ab63602be7b4c5cc9febc3ca821c1d5cd6a1b -size 42700 +oid sha256:5da1fef365731946e08a38250187fe6f3108aff466d7771863c0ba52fb5b7728 +size 42254 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png index 478f3dcb3c..fc66173002 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cb0953ce6c69ad2e49213bf0dbff76a87505f88a092660b71d97ad1e75a5344 -size 45766 +oid sha256:0d5b8a9f7bc2e7f654b018b2f66b5971c21326dd6b64371f330299649daea4c0 +size 45755 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png index 37a67e7aa0..d4ff65b44f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0f655ed143e7b894e37d99fc64c75d44e6483e830055e04609a9da36830278c -size 44670 +oid sha256:2dd6b524bd146b4e66621fea59515e6784055f52f6d1d08c393b5be208e5f29a +size 44657 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png index b41099055e..4264857e69 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac26ca718f2118d88315704ce4bc7960983ab2ac0c394199da2c5b3feeec4438 -size 44688 +oid sha256:73a27b8df68182a3d0ae4872a14688ae10e5651ec85490c1e46186acc2c60028 +size 44671 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png index 836386fc75..f0751a53cd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a9c05a4ace7dc7013e4b02b5e652296399cb2f391ed4bacb2f74d06b68c4be4 -size 46436 +oid sha256:9bedc6d0b55d94586de728cb4039303c9ecbd8b3a090c401331ae1bf6e0ba426 +size 46396 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png index f3ee29437c..f5f4d4cf3c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:997444daf4024495291014f1cae1cee2ba389d160c0ccae706b120dcd908996e -size 45024 +oid sha256:d47a7207e83fe770d4925b91b4bb0eee6cb2c494c46b1f3d5d6d0720464392d6 +size 44992 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png index 5ccda85a79..54890aa7bd 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:527d2c4574c8a5f6946ee94415e30152fdd9e153fd7651b5a40df789e8694d72 -size 43969 +oid sha256:605ea373238bbaa4d3820adf13bf3896d9a5c5b6ceb6c022a7f3b8ae817f921f +size 43927 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png index 60753106e7..385ca39bc9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7da6bfdf7bc9878e1850f8a443adae82a3a4b0aaa5574cec74081ba6f88b879e -size 45622 +oid sha256:c4dab72328925eb45cd529913972670587ea4257d2ff7affd708295d68e783d7 +size 45579 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png index 5549792942..064cfc5b27 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e253e47f998774da8443926990526105fd918b79040955a3d884558a110db8a -size 45538 +oid sha256:76cc029a07c0fb568d9e15864be667478cd9927095f66ec2f590a1466c58a291 +size 45497 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png index e5dac7dc72..3bcd7d1de4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60a989450ffb8ed428d5dd021573b821d238a06f5515f21f9d50b855889ad985 -size 46022 +oid sha256:42a8b9b5abe3a5a3fe61e27924ca75ea7925e857005d5de3cd3ceb81500f73c2 +size 45981 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png index d4143895a2..5dc96aaa2c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37e6049854cd5aaeb50f38c4604e631fb561f0f309d4c58d41becf69a01ca036 -size 46596 +oid sha256:19cc2df1da3b685668afe309bd305d5ac8447d0997c7ad902685d44daa26239f +size 46556 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png index 03bcda4666..2b4718d729 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05a841f7590a33ffea5b09ffc0975bc9090b03cace9e22c330965ce773431211 -size 45849 +oid sha256:5625bb01c24d969adeecdb536e145ba549ef1c10d99e16f7a68c3bd577fbefbc +size 45804 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png index 19d509d513..4f40cab65a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bc90cce90b0648a340e332276fe4395aec673408aaf938bd0d744d45c89dec2 -size 45396 +oid sha256:3bdd0b00fddd9fc62b5c9c97b5ff766591c0a1822730bd249059ed02508a65ee +size 45353 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png index 8027c68a7b..74405c8661 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3ab8eeb4123df1feb35b4d9d9c7fbf166be943edfeb9fa23ac6549463763b10 -size 42853 +oid sha256:fef392cb892bf6dc746ed59f1279bf9558536ef439715d1834bd88260739949d +size 42441 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png index 64acd4e843..dd6c6a993d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3eee4e61c8676a547c3ccbb1d6b9ea2ce0ae30286012de6b3a4f912b908d2e0 -size 42722 +oid sha256:c9b7c26c5f0638dbe7731e239fc16bb2ba4330986af7218688a3a12ede2bfd9d +size 42313 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png index ef06cd3451..02be5d5da0 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02c734eb60efab5050988331527aae294fc6baf0b110e3395103429a79ba059c -size 41424 +oid sha256:4dd437c614c904bf210e5b51fe017f26cf849e7572b09966e834ac6fd429d347 +size 41380 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png index 9462f1336b..7c037817b4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:389fcb65c5334f31b3d66731de2c65128d0a17e7380ca7c45be8a36023719732 -size 46097 +oid sha256:380fbdd2caa485b9fe84a331e64139ccb23998ce1d95158c6345364daa3810b8 +size 46048 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png index f1f9639eae..7b74e5750c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:567cefb525795b66704ccd1738bfe08247004814c947f6c60801588304c768c2 -size 45835 +oid sha256:c4610f8488c8c8edf05e2cd3e09470660b2c4db14650907c28367d2b38bce852 +size 45792 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png index fb830369a5..a2e1108c0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f87512e18ca91237141d97ed03f4725a8a671268daf50f0a8049ee34bdc42a61 -size 45503 +oid sha256:efb795d2808d7ae5e38eda42105101dce377154c794844f752e4cb80dcabd681 +size 45462 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png index 25bcd31599..2d38f9d8b1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ced1cdb2e11bff01ffce233ddf6cf826d6db266739abcd022f83b58d8bec012f -size 38785 +oid sha256:7dca0800e7e68e65bd27814b4c86123da40385d6e85153e003ba58ca7689b5f8 +size 38742 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png index a64f6a2843..1fc42278e2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfd96490ce4509620efd7be72e6ecca219eafe73050417c0c3685339444457f1 -size 43060 +oid sha256:351183a1b0fb44dc42c790a625ee6aa5740b27fa95b138252b72bf67268f5a16 +size 43015 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png index 92b2b86922..da0c4a9dde 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db67d40bae68ec1ece950a06f618606fedc78aa997743b1804db4b69848ba65b -size 43381 +oid sha256:31f92cc4d97c7129bc37da51e6f0a10612d4f61803762b1709c5d72f1cda86f1 +size 43337 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png index 8b143f6b5c..e6cae695cf 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cee9f291a3257f3960ea5d1490f3f219bb0d42b01109b9b398b941524b85971 -size 42383 +oid sha256:8d6a5102a9dd44a6a15dc6b40ec422a115a302506a2b3808a1df84ca34cb323e +size 41972 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png index 2f72417b73..a1947b9c2c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd455b5b492eb88bde4e985ca01ab97e23a337d7ac478782c09dbb10803960f2 -size 43430 +oid sha256:1538aa1f1313df1f6cbbc0ed7ef92d264c85b7de0080492a7cba19bbd235131d +size 43100 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png index a1071426b1..ae9d249cb2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79ac979bcc0339ed30c68c4617f082a9398edf657cc3aea1c80df04bd4b8bab2 -size 46724 +oid sha256:4e15030fc182998a37493228d91c888d6b499b6a684b1a4e15d402c9156a40ba +size 46678 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png index 36a3defc1f..214dee1111 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d71c5e3a8dd2defe2dd37fee1a22921f49bbc9a52a766e5ad082b6595c6ff75 -size 45577 +oid sha256:b773cce165a6410140afbf0daf3ec526233b97ea707811a3794e591704237398 +size 45536 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png index 6c4e522988..41946442cb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ada1e17913092a5e33b5f422a41826dd711102cfb5ab396d5d323e8e0f0b6589 -size 45590 +oid sha256:f28bec4fb21e827f3467bc08ab0f33e917cf76360903b6e7df6282a7589cae7e +size 45545 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en.png deleted file mode 100644 index b96321378c..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9dda468dafd8eb072ad7f3b731e55fbd1f8a4e11e8765f566b929b1d367fd0e0 -size 13693 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatars_Avatars_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatars_Avatars_en.png deleted file mode 100644 index 097602787d..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_DmAvatars_Avatars_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:69e9858a04efe0160908bd0b85990dce8e983db33408ca58752bd4dc1eef6861 -size 13576 From 071d98c66b6e7b4e908b0ba25b4a81228c1a4e81 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 8 May 2026 17:30:32 +0300 Subject: [PATCH 127/471] Render media captions formatting in the media viewer (#6729) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Render media captions formatting in the media viewer * Update screenshots * Trigger actions * Remove unused imports and reformat code --------- Co-authored-by: ElementBot Co-authored-by: Jorge Martín --- .../messages/impl/MessagesFlowNode.kt | 1 + .../libraries/mediaviewer/api/MediaInfo.kt | 14 ++++ libraries/mediaviewer/impl/build.gradle.kts | 3 + .../impl/DefaultMediaViewerEntryPoint.kt | 1 + .../impl/datasource/EventItemFactory.kt | 6 ++ .../impl/local/AndroidLocalMediaFactory.kt | 1 + .../impl/viewer/MediaViewerStateProvider.kt | 80 +++++++++++++++++++ .../impl/viewer/MediaViewerView.kt | 42 +++++++--- .../TimelineMediaGalleryDataSourceTest.kt | 1 + .../mediaviewer/test/FakeLocalMediaFactory.kt | 1 + ....viewer_MediaViewerViewLandscape_17_en.png | 4 +- ....viewer_MediaViewerViewLandscape_18_en.png | 3 + ....viewer_MediaViewerViewLandscape_19_en.png | 3 + ....viewer_MediaViewerViewLandscape_20_en.png | 3 + ....viewer_MediaViewerViewLandscape_21_en.png | 3 + ....viewer_MediaViewerViewLandscape_22_en.png | 3 + ...l.viewer_MediaViewerViewLandscape_3_en.png | 4 +- ...l.viewer_MediaViewerViewLandscape_4_en.png | 4 +- ...ewer.impl.viewer_MediaViewerView_17_en.png | 4 +- ...ewer.impl.viewer_MediaViewerView_18_en.png | 3 + ...ewer.impl.viewer_MediaViewerView_19_en.png | 3 + ...ewer.impl.viewer_MediaViewerView_20_en.png | 3 + ...ewer.impl.viewer_MediaViewerView_21_en.png | 3 + ...ewer.impl.viewer_MediaViewerView_22_en.png | 3 + ...iewer.impl.viewer_MediaViewerView_3_en.png | 4 +- ...iewer.impl.viewer_MediaViewerView_4_en.png | 4 +- 26 files changed, 182 insertions(+), 22 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_19_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_18_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_19_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_20_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_21_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_22_en.png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 7b0515a423..ff46ce0041 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -646,6 +646,7 @@ class MessagesFlowNode( filename = content.filename, fileSize = content.fileSize, caption = content.caption, + formattedCaption = content.formattedCaption, mimeType = content.mimeType, formattedFileSize = content.formattedFileSize, fileExtension = content.fileExtension, diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt index 74b479dc8c..fedf376a60 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt @@ -17,6 +17,7 @@ import kotlinx.parcelize.Parcelize data class MediaInfo( val filename: String, val caption: String?, + val formattedCaption: CharSequence? = null, val mimeType: String, val fileSize: Long?, val formattedFileSize: String, @@ -33,6 +34,7 @@ data class MediaInfo( fun anImageMediaInfo( senderId: UserId? = UserId("@alice:server.org"), caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -40,6 +42,7 @@ fun anImageMediaInfo( filename = "an image file.jpg", fileSize = 4 * 1024 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.Jpeg, formattedFileSize = "4MB", fileExtension = "jpg", @@ -54,6 +57,7 @@ fun anImageMediaInfo( fun aVideoMediaInfo( caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -62,6 +66,7 @@ fun aVideoMediaInfo( filename = "a video file.mp4", fileSize = 14 * 1024 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.Mp4, formattedFileSize = "14MB", fileExtension = "mp4", @@ -77,6 +82,7 @@ fun aVideoMediaInfo( fun aPdfMediaInfo( filename: String = "a pdf file.pdf", caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -84,6 +90,7 @@ fun aPdfMediaInfo( filename = filename, fileSize = 23 * 1024 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.Pdf, formattedFileSize = "23MB", fileExtension = "pdf", @@ -105,6 +112,7 @@ fun anApkMediaInfo( filename = "an apk file.apk", fileSize = 50 * 1024 * 1024, caption = null, + formattedCaption = null, mimeType = MimeTypes.Apk, formattedFileSize = "50MB", fileExtension = "apk", @@ -120,6 +128,7 @@ fun anApkMediaInfo( fun anAudioMediaInfo( filename: String = "an audio file.mp3", caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -129,6 +138,7 @@ fun anAudioMediaInfo( filename = filename, fileSize = 7 * 1024 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.Mp3, formattedFileSize = "7MB", fileExtension = "mp3", @@ -144,6 +154,7 @@ fun anAudioMediaInfo( fun aVoiceMediaInfo( filename: String = "a voice file.ogg", caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -153,6 +164,7 @@ fun aVoiceMediaInfo( filename = filename, fileSize = 3 * 1024 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.Ogg, formattedFileSize = "3MB", fileExtension = "ogg", @@ -168,6 +180,7 @@ fun aVoiceMediaInfo( fun aTxtMediaInfo( filename: String = "a text file.txt", caption: String? = null, + formattedCaption: CharSequence? = null, senderName: String? = null, dateSent: String? = null, dateSentFull: String? = null, @@ -175,6 +188,7 @@ fun aTxtMediaInfo( filename = filename, fileSize = 2 * 1024, caption = caption, + formattedCaption = formattedCaption, mimeType = MimeTypes.PlainText, formattedFileSize = "2kB", fileExtension = "txt", diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index 9c2342ecd9..cd1579d50d 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -25,6 +25,9 @@ android { setupDependencyInjection() dependencies { + implementation(libs.matrix.richtexteditor.compose) + implementation(libs.matrix.richtexteditor) + implementation(projects.libraries.textcomposer.impl) implementation(libs.coroutines.core) implementation(libs.coil.compose) implementation(libs.androidx.media3.exoplayer) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt index e1e112ecfa..138f6382d2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt @@ -32,6 +32,7 @@ class DefaultMediaViewerEntryPoint : MediaViewerEntryPoint { filename = filename, fileSize = null, caption = null, + formattedCaption = null, mimeType = mimeType, formattedFileSize = "", fileExtension = "", diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt index 379c70f960..898a29aa7a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/EventItemFactory.kt @@ -98,6 +98,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), @@ -118,6 +119,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), @@ -139,6 +141,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), @@ -160,6 +163,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), @@ -181,6 +185,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), @@ -202,6 +207,7 @@ class EventItemFactory( filename = type.filename, fileSize = type.info?.size, caption = type.caption, + formattedCaption = type.formattedCaption?.body, mimeType = type.info?.mimetype.orEmpty(), formattedFileSize = type.info?.size?.let { fileSizeFormatter.format(it) }.orEmpty(), fileExtension = fileExtensionExtractor.extractFromName(type.filename), diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index 05cbe40f36..83e8a17cf4 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -97,6 +97,7 @@ class AndroidLocalMediaFactory( filename = fileName, fileSize = fileSize, caption = caption, + formattedCaption = null, formattedFileSize = calculatedFormattedFileSize, fileExtension = fileExtension, senderId = senderId, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index cb1da7b9b2..8cc14d6445 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -195,6 +195,86 @@ open class MediaViewerStateProvider : PreviewParameterProvider ) ) }, + anImageMediaInfo( + senderName = "Bob", + dateSent = "22 NOV, 2024", + formattedCaption = "This is a bold caption", + ).let { + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, it) + ), + mediaInfo = it, + ) + ) + ) + }, + anImageMediaInfo( + senderName = "Charlie", + dateSent = "23 NOV, 2024", + formattedCaption = "This is an italic caption", + ).let { + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, it) + ), + mediaInfo = it, + ) + ) + ) + }, + anImageMediaInfo( + senderName = "Diana", + dateSent = "24 NOV, 2024", + formattedCaption = "This is a code caption", + ).let { + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, it) + ), + mediaInfo = it, + ) + ) + ) + }, + anImageMediaInfo( + senderName = "Eve", + dateSent = "25 NOV, 2024", + formattedCaption = "
This is a quote caption
", + ).let { + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, it) + ), + mediaInfo = it, + ) + ) + ) + }, + anImageMediaInfo( + senderName = "Frank", + dateSent = "26 NOV, 2024", + formattedCaption = "This caption has bold, italic, and code formatting.", + ).let { + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, it) + ), + mediaInfo = it, + ) + ) + ) + }, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 3738f6643b..8bc5088187 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -31,8 +31,11 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -59,10 +62,12 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.core.text.toSpannable import coil3.compose.AsyncImage import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.androidutils.text.safeLinkify import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo @@ -91,7 +96,9 @@ import io.element.android.libraries.mediaviewer.impl.local.LocalMediaView import io.element.android.libraries.mediaviewer.impl.local.PlayableState import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState import io.element.android.libraries.mediaviewer.impl.util.bgCanvasWithTransparency +import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.wysiwyg.compose.EditorStyledText import kotlinx.coroutines.delay import me.saket.telephoto.zoomable.OverzoomEffect import me.saket.telephoto.zoomable.ZoomSpec @@ -242,6 +249,7 @@ fun MediaViewerView( MediaViewerBottomBar( showDivider = dataForPage.mediaInfo.mimeType.isMimeTypeVideo(), caption = dataForPage.mediaInfo.caption, + formattedCaption = dataForPage.mediaInfo.formattedCaption, onHeightChange = { bottomPaddingInPixels = it }, ) } @@ -545,6 +553,7 @@ private fun MediaViewerTopBar( @Composable private fun MediaViewerBottomBar( caption: String?, + formattedCaption: CharSequence?, showDivider: Boolean, onHeightChange: (Int) -> Unit, modifier: Modifier = Modifier, @@ -557,7 +566,7 @@ private fun MediaViewerBottomBar( onHeightChange(it.height) }, ) { - if (caption != null) { + if (caption != null || formattedCaption != null) { if (showDivider) { HorizontalDivider() } @@ -568,15 +577,28 @@ private fun MediaViewerBottomBar( .fillMaxWidth() .heightIn(max = if (hasCompactHeightWindowSize()) maxCaptionHeightLandscape else maxCaptionHeightPortrait), ) { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - .verticalScroll(scrollState) - .navigationBarsPadding(), - text = caption, - style = ElementTheme.typography.fontBodyLgRegular, - ) + val textToRender = when { + formattedCaption != null -> formattedCaption + caption != null -> caption.safeLinkify().toSpannable() + else -> null + } + if (textToRender != null) { + CompositionLocalProvider( + LocalContentColor provides ElementTheme.colors.textPrimary, + LocalTextStyle provides ElementTheme.typography.fontBodyLgRegular + ) { + EditorStyledText( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .verticalScroll(scrollState) + .navigationBarsPadding(), + text = textToRender, + style = ElementRichTextEditorStyle.textStyle(), + releaseOnDetach = false, + ) + } + } if (showBottomShadow) { Box( modifier = Modifier diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt index 528fc1da70..88aefb842c 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/TimelineMediaGalleryDataSourceTest.kt @@ -235,6 +235,7 @@ class TimelineMediaGalleryDataSourceTest { filename = "body.jpg", fileSize = 888L, caption = "body.jpg caption", + formattedCaption = "formatted", mimeType = MimeTypes.Jpeg, formattedFileSize = "888 Bytes", fileExtension = "jpg", diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt index faa27fd0e3..8dbee232df 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeLocalMediaFactory.kt @@ -41,6 +41,7 @@ class FakeLocalMediaFactory( filename = safeName, fileSize = null, caption = null, + formattedCaption = null, mimeType = mimeType ?: fallbackMimeType, formattedFileSize = formattedFileSize ?: fallbackFileSize, fileExtension = fileExtensionExtractor.extractFromName(safeName), diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en.png index 36ecce8294..2e580e55a4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5aa3bca6cd248ac4725fb35aa11a465029dc534b8b167aedbf3e9bc240577e9c -size 654171 +oid sha256:4e8ce597c240a7e72b6811537b6fd24e1bd38714db0ce074377ab3f16eaf0436 +size 656958 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en.png new file mode 100644 index 0000000000..b18a209110 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46d390cfe8d41536cea5e90cb38aa547969ae24d82027c02cfb71c4fbc780247 +size 667948 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_19_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_19_en.png new file mode 100644 index 0000000000..e22d5b8e72 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_19_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bd4f0133fc3a4a159e3eda1f715e4140464b7c67558c551216546d4688f21bc +size 669143 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en.png new file mode 100644 index 0000000000..1023508292 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:178de2176a3932665897486ecbee622083af2d5939f4f1f7f0bdc4667a61e36a +size 668061 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en.png new file mode 100644 index 0000000000..9fa3e43792 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc974dfc6206f389153477d287a69c401ddc154a529cb892227a743acee8ac50 +size 668212 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en.png new file mode 100644 index 0000000000..081ab9a80b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7ac632f2062aa4a13ee46fd2fef6d9e6be25270f4790247131231163670cbe6 +size 670668 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en.png index c63d5a9110..d122817b15 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd5b76aefb0fde0605556e78c7286bf8ee8dd465def0e3095ebc97ce3427eafa -size 666239 +oid sha256:21b8289db279172f042fa902bdbdb70d87104d342a1140357bb1ac2bc18f6590 +size 668482 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png index d404456119..797c1cb2cb 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6870b9c1a5aad4257aa4bd7b13d0be5bea281778061d71c711495aadfaafdb8 -size 206057 +oid sha256:f72964447f2fa66e8df38fcff4a84ada1cf62e1646a2e8a00c61f8f151abfa8c +size 206507 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_17_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_17_en.png index 5b167203f5..be398e06bf 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_17_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bddbe3b0e66e0a2ce49b39321057c94427933de81663f680237a398e9929ba3 -size 442729 +oid sha256:2adfc04e0f7999ba861bf931b853f068067387558e81464e99caba1c2445a3a7 +size 444811 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_18_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_18_en.png new file mode 100644 index 0000000000..057e117409 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0487a6dbc3a2f3bfb79a709c782fb692ef93f37607b4483095ce76f08954580e +size 396582 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_19_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_19_en.png new file mode 100644 index 0000000000..bc7a7cc257 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_19_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29833e4103ecf4bd3e1c5da384d3bcc20031073a14261971825e97bc9f1dad23 +size 397593 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_20_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_20_en.png new file mode 100644 index 0000000000..aa8d934627 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_20_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b544362587868c37edc424b675c6758e7ffcff359fa12c1cfcb874a0502e25de +size 397284 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_21_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_21_en.png new file mode 100644 index 0000000000..26758e047f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_21_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51e183ca6d518d803471ad4eed0ee932397c32a4c1ffb064714b39f13ea2003f +size 396825 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_22_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_22_en.png new file mode 100644 index 0000000000..7f34e6d53c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_22_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b6623e0e784a93d6a3aa220b620efe7d1f9e040aedac7f34d01b24262fe101b +size 401770 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_3_en.png index 167467d515..7d1ba874a3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bd4a96daaa24c01b7d0007fdd14460dc80c65c13d0ccc7a887b04fd90e9fa99 -size 396805 +oid sha256:d56f08f1c37575de90a67ebfeadaf1c891c9ac9e4925af7d048922df9fec5aa9 +size 396815 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png index c44162b0ff..52f17795bb 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0249f33aca3e50713c87a3826e71985991f0996998132c42a374c6169800023 -size 130728 +oid sha256:e3a148746b4f7428688a634f2b1299f0393bf669a665c94fa9af12d7839e72a0 +size 130834 From 0c657c258ad5fb4c9b234c7fc432722d1696dfe0 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 11 May 2026 09:37:59 +0200 Subject: [PATCH 128/471] Sync Strings from Localazy (#6761) Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com> --- .../src/main/res/values-pl/translations.xml | 32 +- .../src/main/res/values-uk/translations.xml | 10 + .../src/main/res/values-el/translations.xml | 6 +- .../src/main/res/values-et/translations.xml | 4 +- .../src/main/res/values-it/translations.xml | 6 +- .../src/main/res/values-pl/translations.xml | 6 +- .../src/main/res/values-uz/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 4 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 3 +- .../src/main/res/values-pl/translations.xml | 7 +- .../src/main/res/values-it/translations.xml | 4 + .../src/main/res/values-pl/translations.xml | 4 + .../src/main/res/values-uk/translations.xml | 4 + .../src/main/res/values-uk/translations.xml | 1 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 19 + .../src/main/res/values-uk/translations.xml | 1 + .../src/main/res/values-el/translations.xml | 5 + .../src/main/res/values-et/translations.xml | 4 + .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 6 + .../src/main/res/values-uk/translations.xml | 5 + .../src/main/res/values-zh/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 2 +- .../src/main/res/values-el/translations.xml | 9 +- .../src/main/res/values-fa/translations.xml | 2 + .../src/main/res/values-it/translations.xml | 3 +- .../src/main/res/values-pl/translations.xml | 13 +- .../src/main/res/values-uk/translations.xml | 11 +- .../src/main/res/values-fa/translations.xml | 16 +- .../src/main/res/values-pl/translations.xml | 26 +- .../src/main/res/values-sk/translations.xml | 10 +- .../src/main/res/values-uk/translations.xml | 4 +- .../src/main/res/values-fa/translations.xml | 8 + .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 5 + .../src/main/res/values-fa/translations.xml | 3 + .../src/main/res/values-it/translations.xml | 8 + .../src/main/res/values-pl/translations.xml | 9 + .../src/main/res/values-uk/translations.xml | 9 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-uk/translations.xml | 2 + .../src/main/res/values-fa/translations.xml | 20 +- .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 42 +- .../src/main/res/values-uk/translations.xml | 5 + .../src/main/res/values-zh/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-be/translations.xml | 1 + .../src/main/res/values-bg/translations.xml | 1 + .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-cy/translations.xml | 1 + .../src/main/res/values-da/translations.xml | 1 + .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-el/translations.xml | 1 + .../src/main/res/values-es/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 1 + .../src/main/res/values-eu/translations.xml | 1 + .../src/main/res/values-fa/translations.xml | 23 +- .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-hr/translations.xml | 1 + .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-in/translations.xml | 1 + .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 1 + .../src/main/res/values-ka/translations.xml | 1 + .../src/main/res/values-ko/translations.xml | 1 + .../src/main/res/values-lt/translations.xml | 1 + .../src/main/res/values-nb/translations.xml | 1 + .../src/main/res/values-nl/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 81 +- .../main/res/values-pt-rBR/translations.xml | 1 + .../src/main/res/values-pt/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-sk/translations.xml | 1 + .../src/main/res/values-sv/translations.xml | 1 + .../src/main/res/values-tr/translations.xml | 1 + .../src/main/res/values-uk/translations.xml | 15 + .../src/main/res/values-ur/translations.xml | 1 + .../src/main/res/values-uz/translations.xml | 1 + .../src/main/res/values-vi/translations.xml | 1 + .../main/res/values-zh-rTW/translations.xml | 1 + .../src/main/res/values-zh/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 4 +- .../src/main/res/values-uk/translations.xml | 2 + .../src/main/res/values-el/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 4 +- .../src/main/res/values-fa/translations.xml | 12 +- .../src/main/res/values-pl/translations.xml | 31 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 43 +- .../src/main/res/values-uk/translations.xml | 6 + .../src/main/res/values-fa/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 12 + .../src/main/res/values-uk/translations.xml | 14 + .../src/main/res/values-fa/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 6 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 + .../src/main/res/values-pl/translations.xml | 2 + .../src/main/res/values-uk/translations.xml | 2 + .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 1 + .../src/main/res/values-uk/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 12 +- .../src/main/res/values-uk/translations.xml | 15 + .../src/main/res/values-be/translations.xml | 1 - .../src/main/res/values-bg/translations.xml | 1 - .../src/main/res/values-cs/translations.xml | 1 - .../src/main/res/values-cy/translations.xml | 1 - .../src/main/res/values-da/translations.xml | 1 - .../src/main/res/values-de/translations.xml | 1 - .../src/main/res/values-el/translations.xml | 9 +- .../src/main/res/values-es/translations.xml | 1 - .../src/main/res/values-et/translations.xml | 17 +- .../src/main/res/values-eu/translations.xml | 1 - .../src/main/res/values-fa/translations.xml | 7 +- .../src/main/res/values-fi/translations.xml | 1 - .../src/main/res/values-fr/translations.xml | 1 - .../src/main/res/values-hr/translations.xml | 1 - .../src/main/res/values-hu/translations.xml | 5 +- .../src/main/res/values-in/translations.xml | 1 - .../src/main/res/values-it/translations.xml | 19 +- .../src/main/res/values-ja/translations.xml | 1 - .../src/main/res/values-ka/translations.xml | 1 - .../src/main/res/values-ko/translations.xml | 1 - .../src/main/res/values-lt/translations.xml | 1 - .../src/main/res/values-nb/translations.xml | 1 - .../src/main/res/values-nl/translations.xml | 1 - .../src/main/res/values-pl/translations.xml | 90 +- .../main/res/values-pt-rBR/translations.xml | 1 - .../src/main/res/values-pt/translations.xml | 1 - .../src/main/res/values-ro/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 1 - .../src/main/res/values-sk/translations.xml | 11 +- .../src/main/res/values-sv/translations.xml | 1 - .../src/main/res/values-tr/translations.xml | 1 - .../src/main/res/values-uk/translations.xml | 58 +- .../src/main/res/values-ur/translations.xml | 1 - .../src/main/res/values-uz/translations.xml | 2 +- .../src/main/res/values-vi/translations.xml | 1 - .../main/res/values-zh-rTW/translations.xml | 1 - .../src/main/res/values-zh/translations.xml | 7 +- .../src/main/res/values/localazy.xml | 4 +- .../de/appnav.root_RootView_Day_0_de.png | 4 +- .../de/appnav.root_RootView_Day_1_de.png | 4 +- ...cline_AcceptDeclineInviteView_Day_1_de.png | 4 +- ...cline_AcceptDeclineInviteView_Day_2_de.png | 4 +- ...mpl.unlock_PinUnlockViewInApp_Day_8_de.png | 3 + ...mpl.unlock_PinUnlockViewInApp_Day_9_de.png | 3 + ...een.impl.unlock_PinUnlockView_Day_8_de.png | 3 + ...een.impl.unlock_PinUnlockView_Day_9_de.png | 3 + ...TimelineItemRoomBeginningView_Day_0_de.png | 4 +- ...ts_TimelineEventTimestampView_Day_3_de.png | 4 +- ...s.impl.timeline_TimelineView_Day_17_de.png | 4 +- ...pl.topbars_MessagesViewTopBar_Day_0_de.png | 4 +- ...impl.root_PreferencesRootViewDark_0_de.png | 4 +- ...impl.root_PreferencesRootViewDark_1_de.png | 4 +- ...impl.root_PreferencesRootViewDark_2_de.png | 4 +- ...impl.root_PreferencesRootViewDark_3_de.png | 4 +- ...impl.root_PreferencesRootViewDark_4_de.png | 4 +- ...impl.root_PreferencesRootViewDark_5_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_0_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_1_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_2_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_3_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_4_de.png | 4 +- ...mpl.root_PreferencesRootViewLight_5_de.png | 4 +- ....api.crash_CrashDetectionView_Day_0_de.png | 4 +- ...ection_RageshakeDialogContent_Day_0_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_0_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_10_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_11_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_12_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_13_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_14_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_15_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_16_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_17_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_18_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_19_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_1_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_20_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_21_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_22_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_2_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_3_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_4_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_5_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_6_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_7_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_8_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_9_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_0_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_10_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_11_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_12_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_13_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_14_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_15_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_16_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_17_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_18_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_19_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_1_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_20_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_21_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_22_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_2_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_3_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_4_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_5_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_6_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_7_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_8_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_9_de.png | 4 +- ...nents_SearchMultipleUsersResultItem_de.png | 4 +- ...mponents_SearchSingleUserResultItem_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_0_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_1_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_2_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_3_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_4_de.png | 4 +- ...tchat.impl.root_StartChatView_Day_5_de.png | 3 - ...rofile.shared_UserProfileView_Day_8_de.png | 4 +- ...mponents_CheckableUnresolvedUserRow_de.png | 4 +- ...eateDmConfirmationBottomSheet_Day_0_de.png | 4 +- ...eateDmConfirmationBottomSheet_Day_1_de.png | 4 +- ....components_SpaceRoomItemView_Day_2_de.png | 4 +- ....components_SpaceRoomItemView_Day_8_de.png | 4 +- ...rix.ui.components_UnresolvedUserRow_de.png | 4 +- screenshots/html/data.js | 2132 +++++++++-------- 248 files changed, 1988 insertions(+), 1448 deletions(-) create mode 100644 features/location/impl/src/main/res/values-el/translations.xml create mode 100644 features/location/impl/src/main/res/values-et/translations.xml create mode 100644 features/location/impl/src/main/res/values-pl/translations.xml create mode 100644 features/location/impl/src/main/res/values-uk/translations.xml create mode 100644 screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_de.png create mode 100644 screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_de.png create mode 100644 screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_8_de.png create mode 100644 screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_9_de.png delete mode 100644 screenshots/de/features.startchat.impl.root_StartChatView_Day_5_de.png 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 0164225ea2..b602cb13d8 100644 --- a/features/createroom/impl/src/main/res/values-pl/translations.xml +++ b/features/createroom/impl/src/main/res/values-pl/translations.xml @@ -3,14 +3,34 @@ "Nowy pokój" "Zaproś znajomych" "Wystąpił błąd w trakcie tworzenia pokoju" - "Tylko zaproszone osoby mogą dołączyć do tego pokoju. Wszystkie wiadomości są szyfrowane end-to-end." + "Nie udało się utworzyć przestrzeni z powodu nieznanego błędu. Spróbuj ponownie później." + "Dodaj nazwę…" + "Nowy pokój" + "Nowa przestrzeń" + "Dołączyć mogą tylko zaproszone osoby." + "Prywatny" "Każdy może znaleźć ten pokój. Możesz to zmienić w ustawieniach 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" - "Każdy może dołączyć do tego pokoju" - "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju." - "Adres pokoju" + "Każdy może dołączyć." + "Publiczny" + "Każdy może poprosić o dołączenie, ale administrator lub moderator musi to zaakceptować." + "Zezwól na prośbę o dołączenie" + "Każdy w %1$s może dołączyć, ale wszyscy pozostali muszą poprosić o dostęp." + "Poproś o dołączenie" + "Dołączyć mogą tylko zaproszone osoby." + "Prywatny" + "Każdy może dołączyć." + "Publiczny" + "Każdy w %1$s może dołączyć." + "Standardowy" + "Kto ma dostęp" + "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, potrzebny jest adres pokoju." + "Adres" "Widoczność pomieszczenia" + "(brak przestrzeni)" + "Nie dodawaj do przestrzeni" + "Nie wybrano przestrzeni" + "Dodaj do przestrzeni" "Temat (opcjonalnie)" + "Dodaj opis…" diff --git a/features/createroom/impl/src/main/res/values-uk/translations.xml b/features/createroom/impl/src/main/res/values-uk/translations.xml index d01da0dc35..a40a2b21a5 100644 --- a/features/createroom/impl/src/main/res/values-uk/translations.xml +++ b/features/createroom/impl/src/main/res/values-uk/translations.xml @@ -3,22 +3,32 @@ "Нова кімната" "Запросити людей" "Під час створення кімнати сталася помилка" + "Простір не вдалося створити через невідому помилку. Спробуйте ще раз пізніше." "Додати назву…" "Нова кімната" "Новий простір" "Можуть приєднатися лише запрошені люди." + "Приватний" "Будь-хто може знайти цю кімнату. Ви можете змінити це в будь-який час у налаштуваннях кімнати." "Приєднатися може будь-хто." + "Публічний" "Будь-хто може подати запит на приєднання, але адміністратор або модератор повинен схвалити запит." "Дозволити запит на приєднання" + "Будь-хто з %1$s може приєднатися, але всі інші повинні подати запит на доступ." + "Запит на приєднання" "Приєднатися можуть лише запрошені особи." + "Приватний" "Приєднатися може будь-хто." + "Публічний" "Приєднатися може будь-хто з %1$s." + "Стандартний" "Хто має доступ" "Вам знадобиться адреса, щоб зробити її видимою в загальнодоступному каталозі." "Адреса" "Видимість кімнати" + "(без пробілу)" + "Не додавати до простору" "Головна" "Додати до простору" "Тема (необов\'язково)" diff --git a/features/deactivation/impl/src/main/res/values-el/translations.xml b/features/deactivation/impl/src/main/res/values-el/translations.xml index ac645f3063..b6a359abbf 100644 --- a/features/deactivation/impl/src/main/res/values-el/translations.xml +++ b/features/deactivation/impl/src/main/res/values-el/translations.xml @@ -1,14 +1,14 @@ - "Παρακαλώ επιβεβαίωσε ότι θες να απενεργοποιήσεις τον λογαριασμό σου. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί." + "Επιβεβαιώστε ότι θέλετε να διαγράψετε τον λογαριασμό σας. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί." "Διαγραφή όλων των μηνυμάτων μου" "Προειδοποίηση: Οι μελλοντικοί χρήστες ενδέχεται να βλέπουν ελλιπείς συνομιλίες." - "Η απενεργοποίηση του λογαριασμού σας είναι %1$s, θα:" + "Η διαγραφή του λογαριασμού σας είναι %1$s, και θα:" "μη αναστρέψιμο" "%1$s τον λογαριασμό σου (δεν μπορείς να συνδεθείς ξανά και το αναγνωριστικό σου δεν μπορεί να επαναχρησιμοποιηθεί)." "Μόνιμη απενεργοποίηση" "Αποχώρησή σας από όλες τις αίθουσες συνομιλίας." "Διαγράψει τα στοιχεία του λογαριασμού σου από τον διακομιστή ταυτότητάς μας." "Τα μηνύματά σου θα εξακολουθούν να είναι ορατά στους εγγεγραμμένους χρήστες, αλλά δεν θα είναι διαθέσιμα σε νέους ή μη εγγεγραμμένους χρήστες εάν επιλέξεις να τα διαγράψεις." - "Απενεργοποίηση λογαριασμού" + "Διαγραφή λογαριασμού" diff --git a/features/deactivation/impl/src/main/res/values-et/translations.xml b/features/deactivation/impl/src/main/res/values-et/translations.xml index 95695fef16..1776e2de17 100644 --- a/features/deactivation/impl/src/main/res/values-et/translations.xml +++ b/features/deactivation/impl/src/main/res/values-et/translations.xml @@ -1,6 +1,6 @@ - "Palun kinnita uuesti, et soovid eemaldada oma konto kasutusest" + "Palun kinnita uuesti, et soovid kustutada oma kasutajakonto. Seda tegevust ei saa tagasi pöörata." "Kustuta kõik minu sõnumid" "Hoiatus: tulevased kasutajad võivad näha poolikuid vestlusi." "Sinu konto kasutusest eemaldamine on %1$s ja sellega:" @@ -10,5 +10,5 @@ "Sind logitakse välja kõikidest jututubadest." "Kustutatakse sinu andmed meie isikutuvastusserverist." "Sinu sõnumid on jätkuvalt nähtavad registreeritud kasutajatele, kuid kui otsustad sõnumid kustutada, siis nad nad pole nähtavad uutele ja registreerimata kasutajatele." - "Eemalda konto kasutusest" + "Kustuta kasutajakonto" diff --git a/features/deactivation/impl/src/main/res/values-it/translations.xml b/features/deactivation/impl/src/main/res/values-it/translations.xml index 7cd484d0e4..e3de1ec8bb 100644 --- a/features/deactivation/impl/src/main/res/values-it/translations.xml +++ b/features/deactivation/impl/src/main/res/values-it/translations.xml @@ -1,14 +1,14 @@ - "Conferma di voler disattivare il tuo account. Questa azione è irreversibile." + "Conferma di voler eliminare il tuo account. Questa azione è irreversibile." "Elimina tutti i miei messaggi" "Attenzione: gli utenti futuri potrebbero vedere conversazioni incomplete." - "La disattivazione del tuo account è %1$s , quindi:" + "L\'eliminazione del tuo account è %1$s, e comporterà:" "irreversibile" "%1$s il tuo account (non puoi riaccedere e il tuo ID non può essere riutilizzato)." "Disattiva permanentemente" "Ti rimuove da tutte le stanze di chat." "Elimina le informazioni del tuo account dal nostro server di identità." "I tuoi messaggi saranno ancora visibili agli utenti registrati, ma non saranno disponibili per gli utenti nuovi o non registrati se decidi di eliminarli." - "Disattivazione dell\'account" + "Elimina account" diff --git a/features/deactivation/impl/src/main/res/values-pl/translations.xml b/features/deactivation/impl/src/main/res/values-pl/translations.xml index bddb6a9037..5778123aa5 100644 --- a/features/deactivation/impl/src/main/res/values-pl/translations.xml +++ b/features/deactivation/impl/src/main/res/values-pl/translations.xml @@ -1,14 +1,14 @@ - "Potwierdź dezaktywacje konta. Tej akcji nie można cofnąć." + "Potwierdź usunięcie konta. Tej akcji nie można cofnąć." "Usuń wszystkie moje wiadomości" "Ostrzeżenie: Przyszli użytkownicy mogą zobaczyć niekompletne rozmowy." - "Dezaktywacja konta jest %1$s, zostanie:" + "Usunięcie konta jest %1$s, co spowoduje:" "nieodwracalna" "%1$s twoje konto (nie będziesz mógł się zalogować, a twoje ID przepadnie)." "Permanentnie wyłączy" "Usunie Ciebie ze wszystkich pokoi rozmów." "Usunięte wszystkie dane konta z naszego serwera tożsamości." "Twoje wiadomości wciąż będą widoczne dla zarejestrowanych użytkowników, ale nie będą dostępne dla nowych lub niezarejestrowanych użytkowników, jeśli je usuniesz." - "Dezaktywuj konto" + "Usuń konto" diff --git a/features/deactivation/impl/src/main/res/values-uz/translations.xml b/features/deactivation/impl/src/main/res/values-uz/translations.xml index d357f38186..e0dcfe59ef 100644 --- a/features/deactivation/impl/src/main/res/values-uz/translations.xml +++ b/features/deactivation/impl/src/main/res/values-uz/translations.xml @@ -10,4 +10,5 @@ "Sizni barcha chat xonalaridan olib tashlash." "Hisobingiz haqidagi axborotni identifikatsiya serverimizdan o‘chirib tashlang." "Xabarlaringiz ro‘yxatdan o‘tgan foydalanuvchilarga ko‘rinadi, lekin ularni o‘chirishni tanlasangiz, yangi yoki ro‘yxatdan o‘tmagan foydalanuvchilarga ko‘rinmaydi." + "Akkauntni o‘chirish" diff --git a/features/ftue/impl/src/main/res/values-pl/translations.xml b/features/ftue/impl/src/main/res/values-pl/translations.xml index 5d77c57994..45ca82ede6 100644 --- a/features/ftue/impl/src/main/res/values-pl/translations.xml +++ b/features/ftue/impl/src/main/res/values-pl/translations.xml @@ -2,8 +2,8 @@ "Nie możesz potwierdzić?" "Utwórz nowy klucz przywracania" - "Zweryfikuj to urządzenie, aby skonfigurować bezpieczne przesyłanie wiadomości." - "Potwierdź, że to Ty" + "Wybierz sposób weryfikacji, aby skonfigurować bezpieczne wiadomości." + "Potwierdź swoją tożsamość cyfrową" "Użyj innego urządzenia" "Użyj klucza przywracania" "Teraz możesz bezpiecznie czytać i wysyłać wiadomości, każdy z kim czatujesz również może ufać temu urządzeniu." diff --git a/features/home/impl/src/main/res/values-et/translations.xml b/features/home/impl/src/main/res/values-et/translations.xml index c1f61bfa29..9d938ed941 100644 --- a/features/home/impl/src/main/res/values-et/translations.xml +++ b/features/home/impl/src/main/res/values-et/translations.xml @@ -7,7 +7,7 @@ "Oleme sinu helisid värskendanud" "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" + "Varunda oma vestlused" "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?" diff --git a/features/home/impl/src/main/res/values-fa/translations.xml b/features/home/impl/src/main/res/values-fa/translations.xml index aec50309d8..b0c6a30bc4 100644 --- a/features/home/impl/src/main/res/values-fa/translations.xml +++ b/features/home/impl/src/main/res/values-fa/translations.xml @@ -4,7 +4,7 @@ "از کار انداختن بهینه سازی" "آگاهی‌ها نمی‌رسند؟" "بازگردانی تاریخچهٔ پیام‌ها و هویت رمزنگاشته‌تان با کلید بازیابی در صورت از دست دادن همهٔ افزاره‌های موجودتان." - "برپایی بازیابی" + "دریافت کلید بازیابی" "برپایی بازیابی" "کلید بازیابی خود را تأیید کنید تا دسترسی به حافظه کلیدها و تاریخچه پیام‌هایتان حفظ شود ." "ورود کلید بازیابیتان" @@ -25,6 +25,7 @@ "آغاز با پیام دادن به کسی." "هنوز گپی وجود ندارد." "علاقه‌مندی‌ها" + "می‌توانید در تنظیمات چت، یک چت را به موارد دلخواه خود اضافه کنید. فعلاً می‌توانید فیلترها را غیرفعال کنید تا چت‌های دیگر خود را ببینید." "هنوز هیچ گپ مورد علاقه‌ای ندارید" "دعوت‌ها" "هیچ دعوت منتظری ندارید." diff --git a/features/home/impl/src/main/res/values-pl/translations.xml b/features/home/impl/src/main/res/values-pl/translations.xml index 73e0b5e52e..0c1b07f27b 100644 --- a/features/home/impl/src/main/res/values-pl/translations.xml +++ b/features/home/impl/src/main/res/values-pl/translations.xml @@ -5,9 +5,9 @@ "Powiadomienia nie dochodzą?" "Sygnał powiadomień został zaktualizowany — jest wyraźniejszy, szybszy i mniej uciążliwy." "Odświeżyliśmy Twoje dźwięki" - "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" + "Twoje czaty są automatycznie archiwizowane za pomocą szyfrowania end-to-end. Aby przywrócić tę kopię zapasową i swoją tożsamość cyfrową, wymagany będzie klucz przywracania." + "Uzyskaj klucz przywracania" + "Utwórz kopię zapasową swoich czatów" "Potwierdź klucz przywracania, aby zachować dostęp do magazynu kluczy i historii wiadomości." "Wprowadź klucz przywracania" "Zapomniałeś klucza przywracania?" @@ -50,6 +50,7 @@ Nie masz żadnych nieprzeczytanych wiadomości!"
"Oznacz jako przeczytane" "Oznacz jako nieprzeczytane" "Ten pokój został ulepszony" + "Twoje przestrzenie" "Wygląda na to, że używasz nowego urządzenia. Zweryfikuj się innym urządzeniem, aby uzyskać dostęp do zaszyfrowanych wiadomości." "Potwierdź, że to Ty" diff --git a/features/invitepeople/impl/src/main/res/values-it/translations.xml b/features/invitepeople/impl/src/main/res/values-it/translations.xml index 979e42de1b..82dc6126f1 100644 --- a/features/invitepeople/impl/src/main/res/values-it/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-it/translations.xml @@ -2,4 +2,8 @@ "Già membro" "Già invitato" + "Al momento non hai conversazioni con questi contatti. Conferma di invitarli in questa stanza prima di continuare." + "Al momento non hai converszioni con questo contatto. Conferma di invitarlo in questa stanza prima di continuare." + "Invita nuovi contatti in questa stanza?" + "Invitare un nuovo contatto in questa stanza?" diff --git a/features/invitepeople/impl/src/main/res/values-pl/translations.xml b/features/invitepeople/impl/src/main/res/values-pl/translations.xml index bfd537bb4b..3e8371c1ab 100644 --- a/features/invitepeople/impl/src/main/res/values-pl/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-pl/translations.xml @@ -2,4 +2,8 @@ "Jest już członkiem" "Już zaproszony" + "Obecnie nie prowadzisz żadnych czatów z tymi kontaktami. Potwierdź zaproszenie, zanim przejdziesz dalej." + "Obecnie nie posiadasz żadnych czatów z tym kontaktem. Potwierdź zaproszenie, zanim przejdziesz dalej." + "Zaprosić nowe kontakty do tego pokoju?" + "Zaprosić nowy kontakt do tego pokoju?" diff --git a/features/invitepeople/impl/src/main/res/values-uk/translations.xml b/features/invitepeople/impl/src/main/res/values-uk/translations.xml index da3ac9fe5b..b7bb3c95d9 100644 --- a/features/invitepeople/impl/src/main/res/values-uk/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-uk/translations.xml @@ -2,4 +2,8 @@ "Уже учасник" "Уже запрошені" + "Наразі у вас немає чатів із цими контактами. Підтвердьте запрошення їх до цієї кімнати, перш ніж продовжувати." + "Наразі у вас немає чатів із цим контактом. Підтвердьте запрошення до цієї кімнати, перш ніж продовжувати." + "Запросити нових контактів до цієї кімнати?" + "Запросити нового контакта до цієї кімнати?" diff --git a/features/joinroom/impl/src/main/res/values-uk/translations.xml b/features/joinroom/impl/src/main/res/values-uk/translations.xml index ec2a24950a..1a83a6ca8c 100644 --- a/features/joinroom/impl/src/main/res/values-uk/translations.xml +++ b/features/joinroom/impl/src/main/res/values-uk/translations.xml @@ -15,6 +15,7 @@ "Вам потрібно отримати запрошення, щоб приєднатися, інакше доступ може бути обмежений." "Забути" "Вам потрібне запрошення, щоб приєднатися" + "Запрошено користувачем" "Доєднатися" "Можливо, вам знадобиться отримати запрошення або стати учасником простору, щоб приєднатися." "Постукати, щоб приєднатися" diff --git a/features/leaveroom/api/src/main/res/values-fa/translations.xml b/features/leaveroom/api/src/main/res/values-fa/translations.xml index 167070891f..ec2cd89314 100644 --- a/features/leaveroom/api/src/main/res/values-fa/translations.xml +++ b/features/leaveroom/api/src/main/res/values-fa/translations.xml @@ -1,5 +1,6 @@ + "آیا مطمئنید که می‌خواهید این مکالمه را ترک کنید؟ این مکالمه عمومی نیست و بدون دعوت نمی‌توانید دوباره به آن بپیوندید." "مطمئنید که می‌خواهید این اتاق را ترک کنید؟ تنها فرد این‌جا هستید. در صورت ترک، هیچ‌کسی از جمله خودتان در آینده نخواهد توانست به آن بپیوندد." "مطمئنید که می‌خواهید این اتاق را ترک کنید؟ این اتاق عمومی نبوده قادر نخواهید بود بدون دعوت دوباره بپیوندید." "گزینش مالکان" diff --git a/features/linknewdevice/impl/src/main/res/values-pl/translations.xml b/features/linknewdevice/impl/src/main/res/values-pl/translations.xml index 18b731a528..ced8955d1d 100644 --- a/features/linknewdevice/impl/src/main/res/values-pl/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-pl/translations.xml @@ -1,16 +1,33 @@ "Skanuj kod QR" + "Otwórz %1$s na laptopie lub komputerze stacjonarnym" "Zeskanuj kod QR za pomocą tego urządzenia" "Gotowy do skanowania" + "Otwórz %1$s na komputerze stacjonarnym, aby uzyskać kod QR" + "Liczby nie pasują do siebie" + "Wprowadź 2-cyfrowy kod" + "Pozwoli to sprawdzić, czy połączenie z drugim urządzeniem jest bezpieczne." + "Wprowadź numer wyświetlany na drugim urządzeniu" "Twój dostawca konta nie obsługuje %1$s." "%1$s nie jest wspierany" + "Twój dostawca konta nie wspiera logowania na nowym urządzeniu za pomocą kodu QR." "Kod QR nie jest wspierany" "Logowanie zostało anulowane na drugim urządzeniu." "Prośba o logowanie została anulowana" "Logowanie wygasło. Spróbuj ponownie." "Logowanie nie zostało ukończone na czas" + "Otwórz %1$s na drugim urządzeniu" "Wybierz %1$s" + "“Zaloguj się za pomocą kodu QR”" + "Zeskanuj kod QR pokazany tutaj za pomocą drugiego urządzenia" + "Otwórz %1$s na drugim urządzeniu" + "Komputer stacjonarny" + "Ładowanie kodu QR…" + "Urządzenie mobilne" + "Jakiego typu urządzenie chcesz powiązać?" + "Spróbuj ponownie i upewnij się, że 2-cyfrowy kod został wpisany prawidłowo. Jeśli liczby wciąż się nie zgadzają, skontaktuj się ze swoim dostawcą konta." + "Liczby nie pasują do siebie" "Nie udało się nawiązać bezpiecznego połączenia z nowym urządzeniem. Twoje istniejące urządzenia są nadal bezpieczne i nie musisz się o nie martwić." "Co teraz?" "Spróbuj zalogować się ponownie za pomocą kodu QR, jeśli byłby to problem z siecią" @@ -23,6 +40,8 @@ "Prośba o logowanie została anulowana" "Logowanie zostało odrzucone na drugim urządzeniu." "Logowanie odrzucone" + "Nie musisz już robić nic więcej." + "Twoje drugie urządzenie jest już zalogowane" "Logowanie wygasło. Spróbuj ponownie." "Logowanie nie zostało ukończone na czas" "Twoje drugie urządzenie nie wspiera logowania się do %s za pomocą kodu QR. diff --git a/features/linknewdevice/impl/src/main/res/values-uk/translations.xml b/features/linknewdevice/impl/src/main/res/values-uk/translations.xml index fe5b1a7460..752b5ead3f 100644 --- a/features/linknewdevice/impl/src/main/res/values-uk/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-uk/translations.xml @@ -26,6 +26,7 @@ "Завантаження QR-коду…" "Мобільний пристрій" "Який тип пристрою ви хочете під\'єднати?" + "Спробуйте ще раз і переконайтеся, що ви правильно ввели двозначний код. Якщо цифри все одно не збігаються, зверніться до свого провайдера облікового запису." "Цифри не збігаються" "Не вдалося встановити безпечне з\'єднання з новим пристроєм. Ваші наявні пристрої досі в безпеці, і вам не потрібно про них турбуватися." "Що тепер?" diff --git a/features/location/impl/src/main/res/values-el/translations.xml b/features/location/impl/src/main/res/values-el/translations.xml new file mode 100644 index 0000000000..8ae7c58c74 --- /dev/null +++ b/features/location/impl/src/main/res/values-el/translations.xml @@ -0,0 +1,5 @@ + + + "Το ιστορικό ζωντανής τοποθεσίας σας θα αποθηκευτεί στην αίθουσα και θα είναι ορατό στα μέλη μετά το τέλος της συνεδρίας." + "Επιλέξτε για πόσο χρονικό διάστημα θα κοινοποιείτε την τρέχουσα τοποθεσία σας." + diff --git a/features/location/impl/src/main/res/values-et/translations.xml b/features/location/impl/src/main/res/values-et/translations.xml new file mode 100644 index 0000000000..53aede1bb2 --- /dev/null +++ b/features/location/impl/src/main/res/values-et/translations.xml @@ -0,0 +1,4 @@ + + + "Vali, kui kaua tahad oma reaalajas jagada." + diff --git a/features/location/impl/src/main/res/values-hu/translations.xml b/features/location/impl/src/main/res/values-hu/translations.xml index b89965485f..0ccb2a2413 100644 --- a/features/location/impl/src/main/res/values-hu/translations.xml +++ b/features/location/impl/src/main/res/values-hu/translations.xml @@ -2,4 +2,5 @@ "Az élő helymeghatározás története a szobában lesz tárolva, és a munkamenet befejezése után is látható marad a tagok számára." "Válassza ki, mennyi ideig szeretné megosztani az aktuális tartózkodási helyét." + "Nincs jogosultsága az élő tartózkodási helyének megosztására ebben a szobában." diff --git a/features/location/impl/src/main/res/values-it/translations.xml b/features/location/impl/src/main/res/values-it/translations.xml index 235a9eba4a..44adc1e3c5 100644 --- a/features/location/impl/src/main/res/values-it/translations.xml +++ b/features/location/impl/src/main/res/values-it/translations.xml @@ -2,4 +2,5 @@ "La cronologia delle tue posizioni in tempo reale verrà archiviata nella stanza e sarà visibile ai membri al termine della sessione." "Scegli per quanto tempo condividere la tua posizione in tempo reale." + "Non hai l\'autorizzazione per condividere la tua posizione in tempo reale in questa stanza" diff --git a/features/location/impl/src/main/res/values-pl/translations.xml b/features/location/impl/src/main/res/values-pl/translations.xml new file mode 100644 index 0000000000..c480d0f43b --- /dev/null +++ b/features/location/impl/src/main/res/values-pl/translations.xml @@ -0,0 +1,6 @@ + + + "Twoja historia lokalizacji na żywo zostanie zapisana w pokoju i będzie widoczna dla członków po zakończeniu sesji." + "Wybierz, jak długo chcesz udostępniać swoją lokalizację na żywo." + "Nie masz uprawnień do udostępniania swojej lokalizacji na żywo w tym pokoju" + diff --git a/features/location/impl/src/main/res/values-uk/translations.xml b/features/location/impl/src/main/res/values-uk/translations.xml new file mode 100644 index 0000000000..3c8155817a --- /dev/null +++ b/features/location/impl/src/main/res/values-uk/translations.xml @@ -0,0 +1,5 @@ + + + "Ваша історія поточного місцезнаходження зберігатиметься у кімнаті та буде доступна учасникам після завершення сеансу." + "Виберіть, як довго ділитися своїм місцезнаходженням." + diff --git a/features/location/impl/src/main/res/values-zh/translations.xml b/features/location/impl/src/main/res/values-zh/translations.xml index 6837d3a1eb..563bba593f 100644 --- a/features/location/impl/src/main/res/values-zh/translations.xml +++ b/features/location/impl/src/main/res/values-zh/translations.xml @@ -2,4 +2,5 @@ "你实时位置历史将存储在房间中,并于会话结束后对其他成员可见。" "选择共享实时位置的时长。" + "你无权在此房内共享实时位置。" diff --git a/features/location/impl/src/main/res/values/localazy.xml b/features/location/impl/src/main/res/values/localazy.xml index ac2ff4b2a0..975bb3c6ea 100644 --- a/features/location/impl/src/main/res/values/localazy.xml +++ b/features/location/impl/src/main/res/values/localazy.xml @@ -2,4 +2,5 @@ "Your live location history will be stored in the room and visible to members after the session ends." "Choose how long to share your live location." + "You do not have permissions to share your live location in this room" diff --git a/features/lockscreen/impl/src/main/res/values-fa/translations.xml b/features/lockscreen/impl/src/main/res/values-fa/translations.xml index bd2000b94d..0575f22221 100644 --- a/features/lockscreen/impl/src/main/res/values-fa/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-fa/translations.xml @@ -23,7 +23,7 @@ "لطفاً یک پین را دو بار وارد کنید" "پین‌ها مطابق نیستند" "برای ادامه باید دوباره وارد شده و پینی جدید ایجاد کنید" - "دارید خارج می‌شوید" + "این دستگاه در حال حذف شدن است" "شما %1$d تلاش برای باز کردن قفل دارید" "شما %1$d تلاش برای باز کردن قفل دارید" diff --git a/features/lockscreen/impl/src/main/res/values-pl/translations.xml b/features/lockscreen/impl/src/main/res/values-pl/translations.xml index 5d61ecb7c6..29691987f6 100644 --- a/features/lockscreen/impl/src/main/res/values-pl/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-pl/translations.xml @@ -23,7 +23,7 @@ Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz ten PIN, zostaniesz wyl "Wprowadź ten sam kod PIN dwa razy" "PIN\'y nie pasują do siebie" "Aby kontynuować, zaloguj się ponownie i utwórz nowy kod PIN" - "Trwa wylogowywanie" + "Trwa usuwanie urządzenia" "Masz %1$d próbę, żeby odblokować" "Masz %1$d próby, żeby odblokować" @@ -36,5 +36,5 @@ Wybierz coś łatwego do zapamiętania. Jeśli zapomnisz ten PIN, zostaniesz wyl "Użyj biometrii" "Użyj kodu PIN" - "Wylogowywanie…" + "Usuwam urządzenie…" diff --git a/features/lockscreen/impl/src/main/res/values-sk/translations.xml b/features/lockscreen/impl/src/main/res/values-sk/translations.xml index 0cfb2e88cd..14687f9272 100644 --- a/features/lockscreen/impl/src/main/res/values-sk/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-sk/translations.xml @@ -36,5 +36,5 @@ Vyberte si niečo zapamätateľné. Ak tento kód PIN zabudnete, budete z aplik "Použiť biometrické údaje" "Použiť PIN" - "Prebieha odhlasovanie…" + "Odoberanie zariadenia…" diff --git a/features/lockscreen/impl/src/main/res/values-uk/translations.xml b/features/lockscreen/impl/src/main/res/values-uk/translations.xml index 5c19889282..25e96003c8 100644 --- a/features/lockscreen/impl/src/main/res/values-uk/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-uk/translations.xml @@ -36,5 +36,5 @@ "Використати біометрію" "Використати PIN-код" - "Вихід…" + "Видалення пристрою…" diff --git a/features/login/impl/src/main/res/values-el/translations.xml b/features/login/impl/src/main/res/values-el/translations.xml index 85640698f3..045465902b 100644 --- a/features/login/impl/src/main/res/values-el/translations.xml +++ b/features/login/impl/src/main/res/values-el/translations.xml @@ -28,7 +28,7 @@ "Ποια είναι η διεύθυνση του διακομιστή σου;" "Επέλεξε το διακομιστή σου" "Δημιουργία λογαριασμού" - "Αυτός ο λογαριασμός έχει απενεργοποιηθεί." + "Αυτός ο λογαριασμός έχει διαγραφεί." "Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης" "Αυτό δεν είναι έγκυρο αναγνωριστικό χρήστη. Αναμενόμενη μορφή: \'@χρήστης:homeserver.org\'" "Αυτός ο διακομιστής έχει ρυθμιστεί ώστε να χρησιμοποιεί διακριτικά ανανέωσης. Αυτά δεν υποστηρίζονται όταν χρησιμοποιείς σύνδεση μέσω κωδικού πρόσβασης." @@ -37,6 +37,13 @@ "Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία." "Καλωσόρισες ξανά!" "Συνδέσου στο %1$s" + "Άνοιγμα του Element Classic" + "Ανοίξτε το Element Classic στη συσκευή σας." + "Μεταβείτε στις Ρυθμίσεις > Ασφάλεια και Απόρρητο" + "Στη Διαχείριση κλειδιών κρυπτογράφησης, επιλέξτε Ανάκτηση κρυπτογραφημένων μηνυμάτων" + "Ακολουθήστε τις οδηγίες για να ενεργοποιήσετε την αποθήκευση κλειδιών" + "Επιστρέψτε στο %1$s" + "Ενεργοποιήστε την αποθήκευση κλειδιών σας πριν προχωρήσετε στο %1$s" "Έκδοση %1$s" "Σύνδεση χειροκίνητα" "Συνδέσου στο %1$s" diff --git a/features/login/impl/src/main/res/values-fa/translations.xml b/features/login/impl/src/main/res/values-fa/translations.xml index d903103c1b..c28c891c9a 100644 --- a/features/login/impl/src/main/res/values-fa/translations.xml +++ b/features/login/impl/src/main/res/values-fa/translations.xml @@ -15,6 +15,7 @@ "تغییر فراهم کنندهٔ حساب" "پلی گپگل" "ما نتوانستیم به این کارساز خانگی برسیم. لطفاً بررسی کنید که URL کارساز اصلی را به درستی وارد کرده اید. اگر URL صحیح است، برای کمک بیشتر با مدیر کارساز خانگی خود تماس بگیرید." + "سرور به دلیل مشکلی در فایل .well-known در دسترس نیست: %1$s" "نشانی کارساز خانگی" "ورود نشانی دامنه." "نشانی کارسازتان چیست؟" @@ -23,6 +24,7 @@ "این حساب حذف شده است." "نام کاربری یا گذرواژه نامعتبر است" "این یک شناسه کاربری معتبر نیست. قالب صحیح: ‪«@user:homeserver.or" + "این سرور برای استفاده از توکن‌های به‌روزرسانی پیکربندی شده است. این توکن‌ها هنگام استفاده از ورود مبتنی بر رمز عبور پشتیبانی نمی‌شوند." "کارساز اصلی انتخاب شده از رمز عبور یا ورود OAuth پشتیبانی نمی کند. لطفا با مدیر خود تماس بگیرید یا یک کارساز خانگی دیگر را انتخاب کنید." "جزییاتتان را وارد کنید" "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است." diff --git a/features/login/impl/src/main/res/values-it/translations.xml b/features/login/impl/src/main/res/values-it/translations.xml index 3753d58390..1f1b51cda8 100644 --- a/features/login/impl/src/main/res/values-it/translations.xml +++ b/features/login/impl/src/main/res/values-it/translations.xml @@ -28,7 +28,7 @@ "Qual è l\'indirizzo del tuo server?" "Seleziona il tuo server" "Crea account" - "Questo account è stato disattivato." + "Questo account è stato eliminato." "Nome utente e/o password errati" "Questo non è un identità utente valida. il formato atteso é: \'@user:homeserver.org\'" "Questo server è configurato per usare i token di aggiornamento. Non sono supportati quando si usa l\'accesso basato su password." @@ -45,6 +45,7 @@ "Torna a %1$s" "Abilita l\'archivio delle chiavi prima di procedere con %1$s" "Versione %1$s" + "Verifica dell\'account" "Accedi manualmente" "Accedi a %1$s" "Accedi con codice QR" 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 b134395adf..ca0d035fe0 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -28,7 +28,7 @@ "Jaki jest adres Twojego serwera?" "Wybierz swój serwer" "Utwórz konto" - "To konto zostało dezaktywowane." + "To konto zostało usunięte." "Nieprawidłowa nazwa użytkownika i/lub hasło" "To nie jest prawidłowy identyfikator użytkownika. Oczekiwany format: \'@user:homeserver.org\'" "Ten serwer został skonfigurowany do korzystania z tokenów odświeżania. Nie są one obsługiwane, gdy korzystasz z hasła." @@ -37,11 +37,20 @@ "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji." "Witaj ponownie!" "Zaloguj się do %1$s" + "Otwórz Element Classic" + "Otwórz Element Classic na swoim urządzeniu" + "Przejdź do Ustawienia > Bezpieczeństwo i prywatność" + "W Zarządzaniu kluczami kryptograficznymi wybierz przywracanie wiadomości szyfrowanych" + "Aby włączyć magazyn kluczy, postępuj zgodnie z instrukcjami" + "Wróć do %1$s" + "Włącz magazyn kluczy zanim przejdziesz do %1$s" "Wersja %1$s" + "Sprawdzanie konta" "Zaloguj się ręcznie" "Zaloguj się do %1$s" "Zaloguj się za pomocą kodu QR" "Utwórz konto" + "Witamy ponownie" "Witamy w %1$s. Szybszy i prostszy niż kiedykolwiek." "Witamy w %1$s. Doładowany, dla szybkości i prostoty." "Be in your element" @@ -60,6 +69,8 @@ "Prośba o logowanie została anulowana" "Logowanie zostało odrzucone na drugim urządzeniu." "Logowanie odrzucone" + "Nie musisz już robić nic więcej." + "Twoje drugie urządzenie jest już zalogowane" "Logowanie wygasło. Spróbuj ponownie." "Logowanie nie zostało ukończone na czas" "Twoje drugie urządzenie nie wspiera logowania się do %s za pomocą kodu QR. diff --git a/features/login/impl/src/main/res/values-uk/translations.xml b/features/login/impl/src/main/res/values-uk/translations.xml index 0d8eca42cf..17632cc4fc 100644 --- a/features/login/impl/src/main/res/values-uk/translations.xml +++ b/features/login/impl/src/main/res/values-uk/translations.xml @@ -28,7 +28,7 @@ "Яка адреса вашого сервера?" "Виберіть свій сервер" "Створити обліковий запис" - "Цей обліковий запис було деактивовано." + "Цей обліковий запис було видалено." "Неправильне ім\'я користувача та/або пароль" "Це недійсний ідентифікатор користувача. Очікуваний формат: \'@user:homeserver.org\'" "Цей сервер налаштований на використання оновлюваних токенів. Вони не підтримуються, якщо використовується вхід за допомогою основі пароля." @@ -37,11 +37,20 @@ "Matrix — це відкрита мережа для безпечної, децентралізованої комунікації." "З поверненням!" "Увійти в %1$s" + "Відкрити Element Classic" + "Відкрийте Element Classic на своєму пристрої" + "Перейдіть до «Налаштування» > «Безпека та конфіденційність»" + "У розділі «Управління криптографічними ключами» виберіть «Відновлення зашифрованих повідомлень»" + "Дотримуйтесь інструкцій, щоб увімкнути сховище ключів" + "Повернутися до %1$s" + "Увімкніть сховище ключів, перш ніж переходити до %1$s" "Версія %1$s" + "Перевірка облікового запису" "Увійти вручну" "Увійти в %1$s" "Увійти за допомогою QR-коду" "Створити обліковий запис" + "З поверненням!" "Ласкаво просимо до найшвидшого %1$s. Заряджений для швидкості та простоти." "Ласкаво просимо до %1$s. Заряджений, для швидкості та простоти." "Будьте у своєму element" diff --git a/features/logout/impl/src/main/res/values-fa/translations.xml b/features/logout/impl/src/main/res/values-fa/translations.xml index d286ded539..a540b9be37 100644 --- a/features/logout/impl/src/main/res/values-fa/translations.xml +++ b/features/logout/impl/src/main/res/values-fa/translations.xml @@ -4,15 +4,15 @@ "برداشتن این افزاره" "برداشتن این افزاره" "برداشتن افزاره…" - "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید پیام‌های رمزنگاشته‌تان را از دست خواهید داد." - "پشتیبان را خاموش کرده‌اید" - "در هنگامی که آفلاین شدید، کلیدهای شما هنوز در حال پشتیبان‌گیری بودند. دوباره متصل شوید ، تا قبل از خروج از کلیدهایتان نسخه پشتیبان‌ گرفته شود." + "این تنها دستگاه شماست. اگر آن را جدا کنید، برای تأیید هویت دیجیتال خود و بازیابی چت‌های رمزگذاری شده‌تان در دفعه بعد که وارد سیستم می‌شوید، به یک کلید بازیابی نیاز خواهید داشت." + "شما در درحال از دست دادن دسترسی به چت‌های رمزگذاری‌شده‌تان هستید." + "وقتی آفلاین شدید، کلیدهای شما هنوز در حال پشتیبان‌گیری بودند. دوباره متصل شوید تا قبل از جدا کردن این دستگاه، از کلیدهایتان پشتیبان‌گیری شود." "کلیدهایتان هنوز در حال پشتیبان گیریند" - "لطفاً پیش از خروج منتظر پایانش شوید." + "لطفاً قبل از خروج از این دستگاه، منتظر بمانید تا این مراحل تکمیل شود." "کلیدهایتان هنوز در حال پشتیبان گیریند" "برداشتن این افزاره" - "شما در آستانه خروج از آخرین جلسه خود هستید. اگر اکنون از سیستم خارج شوید، دسترسی به پیام های رمزگذاری شده تان را از دست خواهید داد." - "بازگردانی برپا نشده" - "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید ممکن است پیام‌های رمزنگاشته‌تان را از دست بدهید." - "کلید بازیابیتان را ذخیره کرده‌اید؟" + "این تنها دستگاه شماست. اگر آن را جدا کنید، برای تأیید هویت دیجیتال خود و بازیابی چت‌های رمزگذاری شده‌تان در دفعه بعد که وارد سیستم می‌شوید، به یک کلید بازیابی نیاز خواهید داشت." + "شما در حال از دست دادن دسترسی به چت‌های رمزگذاری‌شده‌تان هستید." + "این تنها دستگاه شماست. اگر آن را جدا کنید، برای تأیید هویت دیجیتال خود و بازیابی چت‌های رمزگذاری شده‌تان در دفعه بعد که وارد سیستم می‌شوید، به یک کلید بازیابی نیاز خواهید داشت." + "قبل از حذف این دستگاه، مطمئن شوید که به کلید بازیابی خود دسترسی دارید." diff --git a/features/logout/impl/src/main/res/values-pl/translations.xml b/features/logout/impl/src/main/res/values-pl/translations.xml index 46a5c2d6bd..691255f434 100644 --- a/features/logout/impl/src/main/res/values-pl/translations.xml +++ b/features/logout/impl/src/main/res/values-pl/translations.xml @@ -1,18 +1,18 @@ - "Czy na pewno chcesz się wylogować?" - "Wyloguj" - "Wyloguj" - "Wylogowywanie…" - "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych." - "Wyłączyłeś backup" - "Twoje klucze były nadal archiwizowane po przejściu w tryb offline. Połącz się ponownie, aby zapisać w chmurze przed wylogowaniem." + "Czy na pewno chcesz usunąć to urządzenie?" + "Usuń to urządzenie" + "Usuń to urządzenie" + "Usuwam urządzenie…" + "To jest twoje jedyne urządzenie. Jeśli je usuniesz, będziesz potrzebować klucza przywracania, aby potwierdzić swoją tożsamość cyfrową i przywrócić zaszyfrowane czaty przy następnym logowaniu." + "Zamierzasz utracić dostęp do swoich zaszyfrowanych czatów" + "Twoje klucze były nadal archiwizowane po przejściu w tryb offline. Połącz się ponownie, aby zapisać je w chmurze przed usunięciem urządzenia." "Twoje klucze są nadal archiwizowane" - "Zanim się wylogujesz, poczekaj na zakończenie operacji." + "Poczekaj na zakończenie procesu, zanim usuniesz to urządzenie." "Twoje klucze są nadal archiwizowane" - "Wyloguj" - "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych." - "Nie ustawiono przywracania" - "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych." - "Czy zapisałeś swój klucz przywracania?" + "Usuń to urządzenie" + "To jest twoje jedyne urządzenie. Jeśli je usuniesz, będziesz potrzebować klucza przywracania, aby potwierdzić swoją tożsamość cyfrową i przywrócić zaszyfrowane czaty przy następnym logowaniu." + "Zamierzasz utracić dostęp do swoich zaszyfrowanych czatów" + "To jest twoje jedyne urządzenie. Jeśli je usuniesz, będziesz potrzebować klucza przywracania, aby potwierdzić swoją tożsamość cyfrową i przywrócić zaszyfrowane czaty przy następnym logowaniu." + "Upewnij się, że posiadasz dostęp do klucza przywracania przed usunięciem urządzenia" diff --git a/features/logout/impl/src/main/res/values-sk/translations.xml b/features/logout/impl/src/main/res/values-sk/translations.xml index 39301437fb..4fe07b2fa7 100644 --- a/features/logout/impl/src/main/res/values-sk/translations.xml +++ b/features/logout/impl/src/main/res/values-sk/translations.xml @@ -1,16 +1,16 @@ - "Ste si istí, že sa chcete odhlásiť?" - "Odhlásiť sa" - "Odhlásiť sa" - "Prebieha odhlasovanie…" + "Naozaj chcete odstrániť toto zariadenie?" + "Odstrániť toto zariadenie" + "Odstrániť toto zariadenie" + "Odoberanie zariadenia…" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam." "Vypli ste zálohovanie" "Keď ste sa odpojili od internetu, vaše kľúče sa ešte stále zálohovali. Pripojte sa znova k internetu, aby sa vaše kľúče mohli zálohovať pred odhlásením." "Vaše kľúče sa ešte stále zálohujú" "Pred odhlásením počkajte, kým sa to dokončí." "Vaše kľúče sa ešte stále zálohujú" - "Odhlásiť sa" + "Odstrániť toto zariadenie" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam." "Obnovenie nie je nastavené" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, môžete stratiť prístup k svojim šifrovaným správam." diff --git a/features/logout/impl/src/main/res/values-uk/translations.xml b/features/logout/impl/src/main/res/values-uk/translations.xml index 7e23189dc6..f012603533 100644 --- a/features/logout/impl/src/main/res/values-uk/translations.xml +++ b/features/logout/impl/src/main/res/values-uk/translations.xml @@ -1,9 +1,9 @@ - "Ви впевнені, що бажаєте вийти?" + "Ви впевнені, що хочете видалити цей пристрій?" "Вийти" "Вийти" - "Вихід…" + "Видалення пристрою…" "Ви збираєтеся вийти зі свого останнього сеансу. Якщо ви вийдете зараз, ви втратите доступ до своїх зашифрованих повідомлень." "Ви вимкнули резервне копіювання" "Коли ви вийшли з мережі, резервна копія ваших ключів все ще створювалася. Повторно під\'єднайтеся, щоб зберегти резервну копію ключів перед виходом." diff --git a/features/messages/impl/src/main/res/values-fa/translations.xml b/features/messages/impl/src/main/res/values-fa/translations.xml index dcac058d2f..19f55a5559 100644 --- a/features/messages/impl/src/main/res/values-fa/translations.xml +++ b/features/messages/impl/src/main/res/values-fa/translations.xml @@ -56,5 +56,13 @@ "این اتاق جایگزین شده و دیگر فعّال نیست" "دیدن پیام‌های قدیمی" "این اتاق ادامهٔ اتاقی دیگر است" + + "%1$s، %2$s و %3$d سایر" + "%1$s، %2$s و %3$d موارد دیگر" + + + "%1$s در حال تایپ است" + "%1$s در حال تایپ هستند" + "%1$s و %2$s" diff --git a/features/messages/impl/src/main/res/values-pl/translations.xml b/features/messages/impl/src/main/res/values-pl/translations.xml index 18a8af6cb0..0085e1d4ee 100644 --- a/features/messages/impl/src/main/res/values-pl/translations.xml +++ b/features/messages/impl/src/main/res/values-pl/translations.xml @@ -35,7 +35,7 @@ "Nagraj film" "Załącznik" "Zdjęcia i filmy" - "Lokalizacja" + "Udostępnij lokalizację" "Ankieta" "Formatowanie tekstu" "Historia wiadomości jest obecnie niedostępna." diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 4f267d8552..d2022c4882 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -35,7 +35,7 @@ "Nahrať video" "Príloha" "Knižnica fotografií a videí" - "Poloha" + "Zdieľať polohu" "Anketa" "Formátovanie textu" "História správ v tejto miestnosti nie je momentálne k dispozícii" diff --git a/features/messages/impl/src/main/res/values-uk/translations.xml b/features/messages/impl/src/main/res/values-uk/translations.xml index c51744a408..a3035e9e08 100644 --- a/features/messages/impl/src/main/res/values-uk/translations.xml +++ b/features/messages/impl/src/main/res/values-uk/translations.xml @@ -35,7 +35,7 @@ "Записати відео" "Вкладення" "Бібліотека фото та відео" - "Розташування" + "Поділитися місцеперебуванням" "Опитування" "Форматування тексту" "Історія повідомлень наразі недоступна." 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 4e7ba1c26e..c9094f1d31 100644 --- a/features/preferences/impl/src/main/res/values-et/translations.xml +++ b/features/preferences/impl/src/main/res/values-et/translations.xml @@ -11,6 +11,11 @@ "Peida jututubade kutsetest tunnuspildid" "Peida meedia eelvaated ajajoonel" "Katsed" + "Rakenduse seadistused" + + "Iga %1$d meeter" + "Iga %1$d meetrit" + "Sellega laadid fotosid ja videoid kiiremini üles ning vähendad andmemahtu" "Optimeeri meedia kvaliteeti" "Modereerimine ja ohutus" diff --git a/features/preferences/impl/src/main/res/values-fa/translations.xml b/features/preferences/impl/src/main/res/values-fa/translations.xml index 05e73febb4..1a832ca54c 100644 --- a/features/preferences/impl/src/main/res/values-fa/translations.xml +++ b/features/preferences/impl/src/main/res/values-fa/translations.xml @@ -19,12 +19,15 @@ "فراهم کنندهٔ آگاهی‌های ارسالی" "از کار انداختن ویرایشگر متن غنی یا نوشتن دستی مارک‌دون." "رسید‌های خواندن" + "اگر خاموش باشد، رسیدهای خوانده شدن شما برای کسی ارسال نمی‌شود. شما همچنان رسیدهای خوانده شدن را از سایر کاربران دریافت خواهید کرد." "هم‌رسانی حضور" + "اگر خاموش باشد، نمی‌توانید رسیدهای خوانده شدن یا اعلان‌های تایپ را ارسال یا دریافت کنید." "نهفتن همیشگی" "نمایش همیشگی" "در اتاق‌های خصوصی" "رسانه‌های نهفته همواره خواهند توانست با زدن رویشان نمایان شوند" "نمایش رسانه در خط زمانی" + "گزینه مشاهده منبع پیام در جدول زمانی را فعال کنید." "هیچ کاربر مسدودی ندارید" "رفع انسداد" "قادر خواهید بود دوباره همهٔ پیام‌هایش را ببینید." diff --git a/features/preferences/impl/src/main/res/values-it/translations.xml b/features/preferences/impl/src/main/res/values-it/translations.xml index a10dcc9594..7872a2d47d 100644 --- a/features/preferences/impl/src/main/res/values-it/translations.xml +++ b/features/preferences/impl/src/main/res/values-it/translations.xml @@ -11,6 +11,14 @@ "Nascondi gli avatar nelle richieste di invito alle stanze" "Nascondi le anteprime dei media nelle conversazioni" "Labs" + "La distanza che devi percorrere per attivare un aggiornamento." + "Assicurati che l\'opzione \"Posizione precisa\" sia abilitata per questa app. Per modificare l\'autorizzazione, vai in %1$s." + "Impostazioni app" + "Aggiornamenti posizione in tempo reale" + + "Ogni %1$d metro" + "Ogni %1$d metri" + "Carica foto e video più velocemente e riduci l\'utilizzo dei dati" "Ottimizza la qualità dei contenuti multimediali" "Moderazione e Sicurezza" 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 9e2c56e580..606a79df0d 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -11,6 +11,15 @@ "Ukryj awatary w prośbach o dołączenie do pokoju" "Ukryj podglądy multimediów na osi czasu" "Laboratoria" + "Odległość, jaką należy pokonać, aby uruchomić aktualizację." + "Upewnij się, że \"Dokładna lokalizacja\" jest włączona dla tej aplikacji. Aby zmienić to uprawnienie, przejdź do %1$s." + "Ustawienia aplikacji" + "Aktualizacje lokalizacji na żywo" + + "Co %1$d metr" + "Co %1$d metry" + "Co %1$d metrów" + "Przesyłaj zdjęcia i filmy szybciej, zmniejszając zużycie danych" "Optymalizuj jakość multimediów" "Moderacja i bezpieczeństwo" diff --git a/features/preferences/impl/src/main/res/values-uk/translations.xml b/features/preferences/impl/src/main/res/values-uk/translations.xml index d145724af8..eeffc97328 100644 --- a/features/preferences/impl/src/main/res/values-uk/translations.xml +++ b/features/preferences/impl/src/main/res/values-uk/translations.xml @@ -11,6 +11,15 @@ "Сховати аватари у запитах на запрошення до кімнат" "Сховати попередній перегляд медіа у стрічці" "Лабораторії" + "Відстань, яку потрібно пройти, щоб ініціювати оновлення." + "Переконайтеся, що для цього додатка увімкнено функцію «Точна геолокація». Щоб змінити дозвіл, перейдіть на сторінку %1$s." + "Налаштування додатка" + "Оновлення місцезнаходження в реальному часі" + + "Кожен %1$d метр" + "Кожні %1$d метри" + "Кожні %1$d метрів" + "Швидше завантажуйте фотографії та відео та зменшуйте використання даних" "Оптимізуйте медіаякість" "Модерування й безпека" diff --git a/features/rageshake/impl/src/main/res/values-fa/translations.xml b/features/rageshake/impl/src/main/res/values-fa/translations.xml index ba5ab756db..2e27f97ba6 100644 --- a/features/rageshake/impl/src/main/res/values-fa/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fa/translations.xml @@ -7,6 +7,7 @@ "لطفاً مشکل را شرح دهید. چه‌کار کردید؟ انتظار داشتید چه بشود؟ ولی چه شد؟ لطفاً‌تا جای ممکن وارد جزییات شوید." "شرح مشکل…" "ترجیحاً توضیحات را به زبان انگلیسی بنویسید." + "توضیحات خیلی کوتاه است، لطفاً جزئیات بیشتری در مورد آنچه اتفاق افتاده ارائه دهید. متشکرم!" "ارسال رخدادنگارهای خطا" "اجازه به گزارش‌ها" "ارسال تصویر صفحه" diff --git a/features/rageshake/impl/src/main/res/values-uk/translations.xml b/features/rageshake/impl/src/main/res/values-uk/translations.xml index 3999133537..c29306b9bf 100644 --- a/features/rageshake/impl/src/main/res/values-uk/translations.xml +++ b/features/rageshake/impl/src/main/res/values-uk/translations.xml @@ -14,5 +14,7 @@ "Надіслати знімок екрана" "Журнали будуть додані до вашого повідомлення, щоб переконатися, що все працює належним чином. Щоб надіслати повідомлення без журналів, вимкніть це налаштування." "Стався збій %1$s під час останнього користування. Хочете поділитися з нами звітом про збій?" + "Якщо у вас виникають проблеми зі сповіщеннями, надсилання нам правил push-сповіщень допоможе нам визначити першопричину. Зверніть увагу, що ці правила можуть містити приватну інформацію, таку як ваше ім’я користувача або ключові слова, за якими ви отримували сповіщення." + "Налаштування сповіщень" "Переглянути журнали" diff --git a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml index 2bd79bddd2..4bcb764219 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fa/translations.xml @@ -1,14 +1,14 @@ - "فقط مدیران" + "ادمین" "تحریم افراد" - "برداشتن پیام‌ها" - "هرکسی" - "دعوت افراد و پذیرش درخواست‌های پیوستن" - "نظارت اعضا" + "حذف پیام‌ها" + "عضو" + "دعوت کاربران" + "مدیریت اعضا" "پیام‌ها و محتوا" - "مدیرن و ناظران" - "برداشتن افراد و رد درخواست‌های پیوستن" + "ناظم" + "حذف افراد" "تغییر چهرک اتاق" "ویرایش جزییات" "تغییر نام اتاق" @@ -44,8 +44,8 @@ "تحریم نکردن از اتاق" "محروم" "اعضا" - "فقط مدیران" - "مدیرن و ناظران" + "ادمین" + "ناظم" "مالک" "اعضای اتاق" "رفع تحریم %1$s" @@ -62,5 +62,5 @@ "بازنشانی اجازه‌ها؟" "نقش‌ها" "جزییات اتاق" - "نقش‌ها و اجازه‌ها" + "نقش‌ها و مجوزها" diff --git a/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml index e44a1dba21..9c88a2f20e 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-hu/translations.xml @@ -6,6 +6,7 @@ "Üzenetek eltávolítása" "Tag" "Emberek meghívása" + "Valós idejű hely megosztása" "Tér kezelése" "Szobák kezelése" "Tagok kezelése" diff --git a/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml index b1dea12151..ac1366d988 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-it/translations.xml @@ -6,6 +6,7 @@ "Rimuovi messaggi" "Membro" "Invita persone" + "Condividi posizione in tempo reale" "Gestire lo spazio" "Gestisci le stanze" "Gestisci membri" diff --git a/features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml index 44659c47cd..9b570f5261 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-pl/translations.xml @@ -1,17 +1,24 @@ - "Tylko administratorzy" + "Administrator" "Banowanie osób" + "Zmień ustawienia" "Usuń wiadomości" - "Zapraszanie osób i akceptowanie próśb o dołączenie" + "Członek" + "Zaproś osoby" + "Udostępnij lokalizację na żywo" + "Zarządzaj przestrzeniami" + "Zarządzaj pokojami" + "Zarządzaj członkami" "Wiadomości i zawartość" - "Administratorzy i moderatorzy" - "Usuwanie osób i odrzucanie próśb o dołączenie" + "Moderator" + "Usuń osoby" "Zmień awatar pokoju" - "Edytuj pokój" + "Edytuj szczegóły" "Zmień nazwę pokoju" "Zmień temat pokoju" "Wysyłanie wiadomości" + "Uprawnienia" "Edytuj administratorów" "Tej akcji nie będzie można cofnąć. Promujesz użytkownika, który będzie posiadał takie same uprawnienia jak Ty." "Dodać administratora?" @@ -21,7 +28,7 @@ "Nie będzie można cofnąć tej zmiany, jeśli się zdegradujesz. Jeśli jesteś ostatnim uprzywilejowanym użytkownikiem w pokoju, nie będziesz w stanie odzyskać uprawnień." "Zdegradować siebie?" "%1$s (Oczekujące)" - "(Oczekujący)" + "(Oczekujące)" "Administratorzy automatycznie mają uprawnienia moderatora" "Właściciele automatycznie mają uprawnienia administratora." "Edytuj moderatorów" @@ -31,7 +38,14 @@ "Członków" "Masz niezapisane zmiany." "Zapisać zmiany?" - "W tym pokoju nie ma zbanowanych użytkowników." + "Nie ma zbanowanych użytkowników." + + "%1$d zbanowany" + "%1$d zbanowanych" + "%1$d zbanowanych" + + "Sprawdź pisownię lub wyszukaj ponownie" + "Brak wyników dla “%1$s”" "%1$d osoba" "%1$d osoby" @@ -44,8 +58,14 @@ "Odbanuj z pokoju" "Zbanowanych" "Członków" - "Tylko administratorzy" - "Administratorzy i moderatorzy" + + "%1$d zaproszony" + "%1$d zaproszonych" + "%1$d zaproszonych" + + "Oczekuje" + "Administrator" + "Moderator" "Właściciel" "Członkowie pokoju" "Odbanowanie %1$s" @@ -58,10 +78,12 @@ "Wiadomości i zawartość" "Moderatorzy" "Właściciele" - "Resetuj uprawnienia" + "Uprawnienia" + "Zresetuj uprawnienia" "Po zresetowaniu uprawnień utracisz bieżące ustawienia." "Zresetować uprawnienia?" "Role" "Szczegóły pokoju" + "Szczegóły przestrzeni" "Role i uprawnienia" diff --git a/features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml index 7ce3e78387..99f21bb403 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-uk/translations.xml @@ -38,6 +38,11 @@ "У вас є не збережені зміни." "Зберегти зміни?" "Немає заблокованих користувачів." + + "%1$d Заблокований" + "%1$d Заблоковано" + "%1$d Заблоковано" + "Перевірте правопис або спробуйте новий пошук" "Немає результатів за запитом «%1$s»" diff --git a/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml index b28cf8e524..53edd50a6d 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-zh/translations.xml @@ -6,6 +6,7 @@ "移除消息" "成员" "邀请人员" + "共享实时位置" "管理空间" "管理房间" "管理成员" diff --git a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml index e5ab3f1cd7..dc89095786 100644 --- a/features/rolesandpermissions/impl/src/main/res/values/localazy.xml +++ b/features/rolesandpermissions/impl/src/main/res/values/localazy.xml @@ -6,6 +6,7 @@ "Remove messages" "Member" "Invite people" + "Share live location" "Manage space" "Manage rooms" "Manage members" diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml index 9b89f2cef0..5bd33fef17 100644 --- a/features/roomdetails/impl/src/main/res/values-be/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -43,6 +43,7 @@ "Не атрымалася адключыць гук у гэтым пакоі, паўтарыце спробу." "Не ўдалося ўключыць гук у гэтым пакоі. Паўтарыце спробу." "Запрасіць карыстальнікаў" + "Запрасіць" "Пакінуць размову" "Пакінуць пакой" "Уласныя" diff --git a/features/roomdetails/impl/src/main/res/values-bg/translations.xml b/features/roomdetails/impl/src/main/res/values-bg/translations.xml index 55bce6cd12..2c797cb74e 100644 --- a/features/roomdetails/impl/src/main/res/values-bg/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-bg/translations.xml @@ -33,6 +33,7 @@ "Неуспешно заглушаване на тази стая, моля, опитайте отново." "Неуспешно раззаглушаване на тази стая, моля, опитайте отново." "Поканване на хора" + "Поканване" "Напускане на разговора" "Напускане на стаята" "Медия и файлове" diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml index eaf787e4e3..d47a1c709b 100644 --- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -56,6 +56,7 @@ "Nezavírejte aplikaci, dokud neskončíte." "Příprava pozvánek…" "Pozvat přátele" + "Pozvat" "Opustit konverzaci" "Opustit místnost" "Média a soubory" diff --git a/features/roomdetails/impl/src/main/res/values-cy/translations.xml b/features/roomdetails/impl/src/main/res/values-cy/translations.xml index be8ab00639..b8ec88e51a 100644 --- a/features/roomdetails/impl/src/main/res/values-cy/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cy/translations.xml @@ -53,6 +53,7 @@ "Peidiwch â chau\'r ap nes ei fod wedi gorffen." "Wrthi\'n paratoi gwahoddiadau…" "Gwahodd pobl" + "Gwahodd" "Gadael y sgwrs" "Gadael yr ystafell" "Cyfryngau a ffeiliau" diff --git a/features/roomdetails/impl/src/main/res/values-da/translations.xml b/features/roomdetails/impl/src/main/res/values-da/translations.xml index 2217e0b9c6..9f3b3436f0 100644 --- a/features/roomdetails/impl/src/main/res/values-da/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-da/translations.xml @@ -56,6 +56,7 @@ "Luk ikke appen, før den er færdig." "Forbereder invitationer…" "Invitér andre" + "Invitér" "Forlad samtalen" "Forlad rum" "Medier og filer" diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index d810ca0919..c1487d7d98 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -56,6 +56,7 @@ "Schließ die App erst, wenn du fertig bist." "Einladungen werden vorbereitet…" "Nutzer einladen" + "Einladen" "Unterhaltung verlassen" "Verlassen" "Medien und Dateien" diff --git a/features/roomdetails/impl/src/main/res/values-el/translations.xml b/features/roomdetails/impl/src/main/res/values-el/translations.xml index c1b108a524..c1be20d7f5 100644 --- a/features/roomdetails/impl/src/main/res/values-el/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-el/translations.xml @@ -56,6 +56,7 @@ "Μην κλείσετε την εφαρμογή μέχρι να τελειώσει." "Προετοιμασία προσκλήσεων…" "Πρόσκληση ατόμων" + "Πρόσκληση" "Αποχώρηση από τη συζήτηση" "Αποχώρηση από την αίθουσα" "Πολυμέσα και αρχεία" diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index 716e3ed32a..6b92e48884 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -47,6 +47,7 @@ "No se ha podido silenciar esta sala, inténtalo de nuevo." "Error al dejar de silenciar esta sala, por favor inténtalo de nuevo." "Invitar personas" + "Invitar" "Salir de la conversación" "Salir de la sala" "Medios y archivos" diff --git a/features/roomdetails/impl/src/main/res/values-et/translations.xml b/features/roomdetails/impl/src/main/res/values-et/translations.xml index d5d3af8563..f0c8c58b1f 100644 --- a/features/roomdetails/impl/src/main/res/values-et/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-et/translations.xml @@ -56,6 +56,7 @@ "Ära sulge rakendust enne, kui tegevus on lõppenud." "Valmistan kutseid ette…" "Kutsu osalejaid" + "Kutsu" "Lahku vestlusest" "Lahku jututoast" "Meedia ja failid" diff --git a/features/roomdetails/impl/src/main/res/values-eu/translations.xml b/features/roomdetails/impl/src/main/res/values-eu/translations.xml index 7fb07cf2a4..f299131852 100644 --- a/features/roomdetails/impl/src/main/res/values-eu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-eu/translations.xml @@ -44,6 +44,7 @@ "Ezin izan da gela mututu; saiatu berriro." "Ezin izan da gela mututzeari utzi; saiatu berriro." "Gonbidatu jendea" + "Gonbidatu" "Utzi elkarrizketa" "Atera gelatik" "Multimedia eta fitxategiak" diff --git a/features/roomdetails/impl/src/main/res/values-fa/translations.xml b/features/roomdetails/impl/src/main/res/values-fa/translations.xml index 364c94c6f2..bb7cb490b8 100644 --- a/features/roomdetails/impl/src/main/res/values-fa/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fa/translations.xml @@ -4,15 +4,15 @@ "هنگام به‌روز کردن تنظیمات آگاهی خطایی رخ داد." "کارساز خانگیتان از این گزینه در اتاق‌های رمز شده پشتیبانی نمی‌کند. ممکن است در برخی اتاق‌ها آگاه نشوید." "نظرسنجی‌ها" - "فقط مدیران" + "ادمین" "تحریم افراد" - "برداشتن پیام‌ها" - "هرکسی" - "دعوت افراد و پذیرش درخواست‌های پیوستن" - "نظارت اعضا" + "حذف پیام‌ها" + "عضو" + "دعوت کاربران" + "مدیریت اعضا" "پیام‌ها و محتوا" - "مدیرن و ناظران" - "برداشتن افراد و رد درخواست‌های پیوستن" + "ناظم" + "حذف افراد" "تغییر چهرک اتاق" "ویرایش جزییات" "تغییر نام اتاق" @@ -51,6 +51,7 @@ "کاره را تا زمان پایانش نبندید." "آماده سازی دعوت‌ها…" "دعوت افراد" + "دعوت" "ترک گفت‌وگو" "ترک اتاق" "رسانه‌ها و پرونده‌ها" @@ -60,7 +61,7 @@ "پیام‌های سنجاق شده" "نمایه" "درخواست‌های پیوستن" - "نقش‌ها و اجازه‌ها" + "نقش‌ها و مجوزها" "امنیت و محرمانگی" "امنیت" "هم‌رسانی اتاق" @@ -79,8 +80,8 @@ "تحریم نکردن از اتاق" "محروم" "اعضا" - "فقط مدیران" - "مدیرن و ناظران" + "ادمین" + "ناظم" "مالک" "اعضای اتاق" "رفع تحریم %1$s" @@ -111,7 +112,7 @@ "بازنشانی اجازه‌ها؟" "نقش‌ها" "جزییات اتاق" - "نقش‌ها و اجازه‌ها" + "نقش‌ها و مجوزها" "افزودن نشانی اتاق" "درخواست دعوت" "بله. به کار انداختن رمزنگاری" diff --git a/features/roomdetails/impl/src/main/res/values-fi/translations.xml b/features/roomdetails/impl/src/main/res/values-fi/translations.xml index 110790fac1..0d07ff4eae 100644 --- a/features/roomdetails/impl/src/main/res/values-fi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fi/translations.xml @@ -56,6 +56,7 @@ "Älä sulje sovellusta ennen kuin se on valmis." "Valmistellaan kutsuja…" "Kutsu henkilöitä" + "Kutsu" "Poistu keskustelusta" "Poistu huoneesta" "Media ja tiedostot" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index 0fad68c934..3d4283145c 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -56,6 +56,7 @@ "Ne fermez pas l’application avant que l’opération soit terminée." "Préparation des invitations…" "Inviter des amis" + "Inviter" "Quitter la discussion" "Quitter le salon" "Médias et fichiers" diff --git a/features/roomdetails/impl/src/main/res/values-hr/translations.xml b/features/roomdetails/impl/src/main/res/values-hr/translations.xml index 1bc264ca10..73b6866249 100644 --- a/features/roomdetails/impl/src/main/res/values-hr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hr/translations.xml @@ -56,6 +56,7 @@ "Ne zatvarajte aplikaciju dok se ne završi." "Priprema pozivnica…" "Pozovi osobe" + "Pozovi" "Napusti razgovor" "Napusti sobu" "Mediji i datoteke" 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 1dc523f319..937c91b68d 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -56,6 +56,7 @@ "Ne zárja be az alkalmazást, amíg nem végzett." "Meghívók előkészítése…" "Ismerősök meghívása" + "Meghívás" "Beszélgetés elhagyása" "Szoba elhagyása" "Média és fájlok" diff --git a/features/roomdetails/impl/src/main/res/values-in/translations.xml b/features/roomdetails/impl/src/main/res/values-in/translations.xml index 41bf3d2826..2cce124321 100644 --- a/features/roomdetails/impl/src/main/res/values-in/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-in/translations.xml @@ -53,6 +53,7 @@ "Jangan tutup aplikasi tunggu hingga selesai." "Mempersiapkan undangan…" "Undang orang-orang" + "Undang" "Tinggalkan percakapan" "Tinggalkan ruangan" "Media dan berkas" diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml index 22ec99f2b5..044721c345 100644 --- a/features/roomdetails/impl/src/main/res/values-it/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml @@ -56,6 +56,7 @@ "Non chiudere l\'app fino al completamento." "Preparazione degli inviti…" "Invita persone" + "Invita" "Abbandona la conversazione" "Esci dalla stanza" "File e contenuti multimediali" diff --git a/features/roomdetails/impl/src/main/res/values-ja/translations.xml b/features/roomdetails/impl/src/main/res/values-ja/translations.xml index 70b3255437..b6c7608793 100644 --- a/features/roomdetails/impl/src/main/res/values-ja/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ja/translations.xml @@ -56,6 +56,7 @@ "終了するまでアプリを閉じないでください。" "招待を準備中…" "ユーザーを招待" + "招待" "会話を退出" "ルームを退出" "ファイルとメディア" diff --git a/features/roomdetails/impl/src/main/res/values-ka/translations.xml b/features/roomdetails/impl/src/main/res/values-ka/translations.xml index 3ffc962603..317f62313d 100644 --- a/features/roomdetails/impl/src/main/res/values-ka/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ka/translations.xml @@ -39,6 +39,7 @@ "ამ ოთახის დადუმება ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა." "ამ ოთახის დადუმების მოხსნა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა." "ხალხის მოწვევა" + "მოწვევა" "საუბრის დატოვება" "ოთახის დატოვება" "მორგებული" diff --git a/features/roomdetails/impl/src/main/res/values-ko/translations.xml b/features/roomdetails/impl/src/main/res/values-ko/translations.xml index 2c6462c4a7..8c0db6eefd 100644 --- a/features/roomdetails/impl/src/main/res/values-ko/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ko/translations.xml @@ -56,6 +56,7 @@ "작업이 완료될 때까지 앱을 닫지 마세요." "초대 준비중…" "사람 초대하기" + "초대" "대화에서 나가기" "방 떠나기" "미디어 및 파일" diff --git a/features/roomdetails/impl/src/main/res/values-lt/translations.xml b/features/roomdetails/impl/src/main/res/values-lt/translations.xml index 84f74042da..9bdaeb86e5 100644 --- a/features/roomdetails/impl/src/main/res/values-lt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-lt/translations.xml @@ -8,6 +8,7 @@ "Žinutės yra užrakintos. Tik Jūs ir gavėjai turite unikalius raktus joms atrakinti." "Įjungtas žinučių šifravimas" "Pakviesti žmonių" + "Kviesti" "Palikti pokalbį" "Išeiti iš kambario" "Pasirinktinis" diff --git a/features/roomdetails/impl/src/main/res/values-nb/translations.xml b/features/roomdetails/impl/src/main/res/values-nb/translations.xml index e522f5cd79..52ea87bc3d 100644 --- a/features/roomdetails/impl/src/main/res/values-nb/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nb/translations.xml @@ -56,6 +56,7 @@ "Ikke lukk appen før den er ferdig." "Forbereder invitasjoner…" "Inviter folk" + "Inviter" "Forlat samtalen" "Forlat rommet" "Medier og filer" diff --git a/features/roomdetails/impl/src/main/res/values-nl/translations.xml b/features/roomdetails/impl/src/main/res/values-nl/translations.xml index 9234921c35..e9f03d3a57 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -43,6 +43,7 @@ "Het dempen van deze kamer is mislukt. Probeer het opnieuw." "Het dempen opheffen voor deze kamer is mislukt. Probeer het opnieuw." "Mensen uitnodigen" + "Uitnodigen" "Gesprek verlaten" "Kamer verlaten" "Media en bestanden" diff --git a/features/roomdetails/impl/src/main/res/values-pl/translations.xml b/features/roomdetails/impl/src/main/res/values-pl/translations.xml index e22636290b..7af5d8e557 100644 --- a/features/roomdetails/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pl/translations.xml @@ -1,19 +1,24 @@ - "Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju." - "Adres pokoju" + "Nowi członkowie nie widzą historii" + "Nowi członkowie widzą historię" + "Każdy może przeglądać historię" + "Aby pokój był widoczny w katalogu pokoi publicznych, potrzebny jest adres pokoju." + "Edytuj adres" "Wystąpił błąd podczas aktualizacji ustawienia powiadomień." "Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi." "Ankiety" - "Tylko administratorzy" + "Administrator" "Banowanie osób" "Usuń wiadomości" - "Zapraszanie osób i akceptowanie próśb o dołączenie" + "Członek" + "Zaproś osoby" + "Zarządzaj członkami" "Wiadomości i zawartość" - "Administratorzy i moderatorzy" - "Usuwanie osób i odrzucanie próśb o dołączenie" + "Moderator" + "Usuń osoby" "Zmień awatar pokoju" - "Edytuj pokój" + "Edytuj szczegóły" "Zmień nazwę pokoju" "Zmień temat pokoju" "Wysyłanie wiadomości" @@ -26,7 +31,7 @@ "Nie będzie można cofnąć tej zmiany, jeśli się zdegradujesz. Jeśli jesteś ostatnim uprzywilejowanym użytkownikiem w pokoju, nie będziesz w stanie odzyskać uprawnień." "Zdegradować siebie?" "%1$s (Oczekujące)" - "(Oczekujący)" + "(Oczekujące)" "Administratorzy automatycznie mają uprawnienia moderatora" "Właściciele automatycznie mają uprawnienia administratora." "Edytuj moderatorów" @@ -40,7 +45,7 @@ "Szyfrowany" "Nieszyfrowany" "Pokój publiczny" - "Edytuj pokój" + "Edytuj szczegóły" "Wystąpił nieznany błąd i nie można było zmienić informacji." "Nie można zaktualizować pokoju" "Wiadomości są zabezpieczone kłódkami. Tylko Ty i odbiorcy macie unikalne klucze do ich odblokowania." @@ -51,6 +56,7 @@ "Nie zamykaj aplikacji przed zakończeniem." "Przygotowywanie zaproszeń…" "Zaproś znajomych" + "Zaproś" "Opuść rozmowę" "Opuść pokój" "Media i pliki" @@ -61,13 +67,21 @@ "Profil" "Prośby o dołączenie" "Role i uprawnienia" + "Nazwa" "Bezpieczeństwo i prywatność" "Bezpieczeństwo" "Udostępnij pokój" "Informacje pokoju" "Temat" "Aktualizuję pokój…" - "W tym pokoju nie ma zbanowanych użytkowników." + "Nie ma zbanowanych użytkowników." + + "%1$d zbanowany" + "%1$d zbanowanych" + "%1$d zbanowanych" + + "Sprawdź pisownię lub wyszukaj ponownie" + "Brak wyników dla “%1$s”" "%1$d osoba" "%1$d osoby" @@ -80,8 +94,14 @@ "Odbanuj z pokoju" "Zbanowanych" "Członków" - "Tylko administratorzy" - "Administratorzy i moderatorzy" + + "%1$d zaproszony" + "%1$d zaproszonych" + "%1$d zaproszonych" + + "Oczekuje" + "Administrator" + "Moderator" "Właściciel" "Członkowie pokoju" "Odbanowanie %1$s" @@ -108,15 +128,18 @@ "Wiadomości i zawartość" "Moderatorzy" "Właściciele" - "Resetuj uprawnienia" + "Uprawnienia" + "Zresetuj uprawnienia" "Po zresetowaniu uprawnień utracisz bieżące ustawienia." "Zresetować uprawnienia?" "Role" "Szczegóły pokoju" "Role i uprawnienia" - "Dodaj adres pokoju" - "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić żądanie." + "Dodaj adres" + "Każdy w autoryzowanych przestrzeniach może dołączyć, ale wszyscy inni muszą poprosić o dostęp." + "Każdy musi poprosić o dostęp." "Poproś o dołączenie" + "Każdy w %1$s może dołączyć, ale wszyscy pozostali muszą poprosić o dostęp." "Tak, włącz szyfrowanie" "Po włączeniu szyfrowanie pokoju nie może zostać wyłączone, a historia wiadomości będzie widoczna tylko dla członków od momentu, w którym dołączyli lub zostali zaproszeni. Nikt poza członkami pokoju nie będzie mógł czytać wiadomości. Może to wpłynąć na prawidłowe działanie botów lub mostków. @@ -125,23 +148,31 @@ Odradzamy włączanie szyfrowania dla pokoi, które każdy może znaleźć i do "Po włączeniu szyfrowania nie można wyłączyć." "Szyfrowanie" "Włącz szyfrowanie end-to-end" - "Każdy może znaleźć i dołączyć" + "Każdy może dołączyć." "Każdy" - "Tylko osoby z zaproszeniem mogą dołączyć" - "Tylko zaproszenie" - "Dostęp do pokoju" + "Wybierz, którzy członkowie przestrzeni mogą dołączyć do tego pokoju bez zaproszenia. %1$s" + "Zarządzaj przestrzeniami" + "Tylko zaproszone osoby mogą dołączyć" + "Tylko na zaproszenie" + "Dostęp" + "Każdy w autoryzowanych przestrzeniach może dołączyć." + "Każdy w %1$s może dołączyć." + "Członkowie przestrzeni" "Przestrzenie nie są obecnie wspierane" - "Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju." - "Adres pokoju" + "Aby pokój był widoczny w katalogu pokoi publicznych, potrzebny jest adres pokoju." + "Adres" "Zezwól na znalezienie tego pokoju wyszukując %1$s w katalogu pokoi publicznych" + "Zezwól, by inni mogli Cię znaleźć, przeszukując katalog publiczny." "Widoczny w katalogu pokoi publicznych" - "Ktokolwiek" + "Każdy (historia jest publiczna)" + "Zmiany nie zmienią przeszłych wiadomości, tylko nowe. %1$s" "Kto może czytać historię" - "Od momentu kiedy członkowie zostali zaproszeni" - "Członkowie od momentu włączenia tej opcji" + "Członkowie od kiedy zostali zaproszeni" + "Członkowie (cała historia)" "Adresy pokoju umożliwiają łatwe znalezienie i dołączenie do pokojów. Również możesz się zdecydować na upublicznienie Twojego serwera w katalogu pokoi publicznych." "Publikowanie pokoju" - "Widoczność pokoju" + "Adresy pokoi pomagają w znalezieniu i dołączeniu do pokoi i przestrzeni. Umożliwiają również łatwe udostępnianie ich innym." + "Widoczność" "Bezpieczeństwo i prywatność" diff --git a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml index ddbbe0f601..da98e7bd5a 100644 --- a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml @@ -53,6 +53,7 @@ "Não feche o aplicativo até terminar." "Preparando convites…" "Convidar pessoas" + "Convidar" "Sair da conversa" "Sair da sala" "Mídia e arquivos" diff --git a/features/roomdetails/impl/src/main/res/values-pt/translations.xml b/features/roomdetails/impl/src/main/res/values-pt/translations.xml index 592cba34f6..c5a9be9a4c 100644 --- a/features/roomdetails/impl/src/main/res/values-pt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt/translations.xml @@ -53,6 +53,7 @@ "Não feches a aplicação até concluir." "A preparar convites…" "Convidar pessoas" + "Convidar" "Sair da conversa" "Sair da sala" "Multimédia e ficheiros" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index 2eac873575..0374c64314 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -53,6 +53,7 @@ "Nu închideți aplicația până nu se termină." "Se pregătesc invitațiile…" "Invitați prieteni" + "Invitați" "Părăsiți conversația" "Părăsiți camera" "Media și fișiere" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 7ad44fff9d..bd683009ad 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -56,6 +56,7 @@ "Не закрывайте приложение, пока не закончите." "Подготовка приглашений…" "Пригласить в комнату" + "Пригласить" "Покинуть беседу" "Покинуть комнату" "Медиа и файлы" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index 225dcedac3..6a27e375ee 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -53,6 +53,7 @@ "Nezatvárajte aplikáciu, kým sa neukončí pozývanie." "Príprava pozvánok…" "Pozvať ľudí" + "Pozvať" "Opustiť konverzáciu" "Opustiť miestnosť" "Médiá a súbory" diff --git a/features/roomdetails/impl/src/main/res/values-sv/translations.xml b/features/roomdetails/impl/src/main/res/values-sv/translations.xml index 795dc97106..02574e77e9 100644 --- a/features/roomdetails/impl/src/main/res/values-sv/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sv/translations.xml @@ -53,6 +53,7 @@ "Stäng inte appen förrän det är klart." "Förbereder inbjudningar …" "Bjud in personer" + "Bjud in" "Lämna konversation" "Lämna rum" "Media och filer" diff --git a/features/roomdetails/impl/src/main/res/values-tr/translations.xml b/features/roomdetails/impl/src/main/res/values-tr/translations.xml index 16137ce753..8d11b8035a 100644 --- a/features/roomdetails/impl/src/main/res/values-tr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-tr/translations.xml @@ -46,6 +46,7 @@ "Bu odayı sessize alma başarısız oldu, lütfen tekrar deneyin." "Bu odanın sesi açılamadı, lütfen tekrar deneyin." "Kişileri davet et" + "Davet et" "Sohbeti bırak" "Odadan ayrıl" "Medya ve dosyalar" diff --git a/features/roomdetails/impl/src/main/res/values-uk/translations.xml b/features/roomdetails/impl/src/main/res/values-uk/translations.xml index 46adfa54d5..1565e0ec48 100644 --- a/features/roomdetails/impl/src/main/res/values-uk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uk/translations.xml @@ -1,5 +1,8 @@ + "Нові учасники не бачать історії" + "Нові учасники бачать історію" + "Будь-хто може переглянути історію" "Вам знадобиться адреса кімнати, щоб зробити її видимою в каталозі." "Змінити адресу" "Під час оновлення налаштувань сповіщень сталася помилка." @@ -53,6 +56,7 @@ "Не закривайте застосунок доки не завершите." "Приготування запрошень…" "Запросити людей" + "Запросити" "Залишити розмову" "Вийти з кімнати" "Медіа та файли" @@ -71,6 +75,11 @@ "Тема" "Оновлення кімнати…" "Немає заблокованих користувачів." + + "%1$d Заблокований" + "%1$d Заблоковано" + "%1$d Заблоковано" + "Перевірте правопис або спробуйте новий пошук" "Немає результатів за запитом «%1$s»" @@ -127,8 +136,10 @@ "Деталі кімнати" "Ролі та дозволи" "Додати адресу" + "Будь-хто в авторизованих просторах може приєднатися, але всі інші повинні подати запит на доступ." "Усі повинні запитувати доступ." "Запит на приєднання" + "Будь-хто з %1$s може приєднатися, але всі інші повинні подати запит на доступ." "Так, увімкнути шифрування" "Після ввімкнення шифрування кімнати, його неможливо вимкнути, історію повідомлень бачитимуть лише учасники кімнати, яких було запрошено або які приєдналися до кімнати. Ніхто, крім учасників кімнати, не зможе прочитати повідомлення. Це може перешкоджати коректній роботі ботів і мостів. @@ -144,11 +155,14 @@ "Приєднатися можуть лише запрошені люди." "Лише запрошені" "Доступ" + "Долучитися може будь-хто, хто має доступ до авторизованих просторів." "Долучитися може будь-хто з %1$s." + "Учасники простору" "Простори наразі не підтримуються" "Вам знадобиться адреса кімнати, щоб зробити її видимою в каталозі." "Адреса" "Дозвольте, щоб цю кімнату можна було знайти за допомогою пошуку в каталозі загальнодоступних кімнат %1$s " + "Дозвольте знаходити вас за допомогою пошуку в публічному каталозі." "Видима в загальному каталозі" "Будь-хто (загальнодоступна історія)" "Зміни не вплинуть на попередні повідомлення, лише на нові. %1$s" @@ -158,6 +172,7 @@ "Адреси кімнат — це спосіб знайти кімнату та отримати до неї доступ. Це також гарантує, що ви можете легко поділитися своєю кімнатою з іншими. Ви можете опублікувати свою кімнату в каталозі загальнодоступних кімнат вашого домашнього сервера." "Публікація в кімнаті" + "Адреси — це спосіб знаходити кімнати та простори та отримувати до них доступ. Це також гарантує, що ви зможете легко ділитися ними з іншими." "Видимість" "Безпека й приватність" diff --git a/features/roomdetails/impl/src/main/res/values-ur/translations.xml b/features/roomdetails/impl/src/main/res/values-ur/translations.xml index 3715bb91ff..d5ab09dd18 100644 --- a/features/roomdetails/impl/src/main/res/values-ur/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ur/translations.xml @@ -43,6 +43,7 @@ "اس کمرے کو خاموش کرنے میں ناکام، برائے مہربانی دوبارہ کوشش کریں۔" "اس کمرے کو غیر خاموش کرنے میں ناکام، برائے مہربانی دوبارہ کوشش کریں۔" "لوگوں کو مدعو کریں" + "مدعو کریں" "گفتگو چھوڑیں" "کمرہ چھوڑ دیں" "حسب ضرورت" diff --git a/features/roomdetails/impl/src/main/res/values-uz/translations.xml b/features/roomdetails/impl/src/main/res/values-uz/translations.xml index 1b4718ee44..fa9408b3ba 100644 --- a/features/roomdetails/impl/src/main/res/values-uz/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uz/translations.xml @@ -56,6 +56,7 @@ "Tugallanmaguncha ilovani yopmang." "Taklifnomalar tayyorlanmoqda…" "Odamlarni taklif qiling" + "Taklif qilish" "Suhbatni tark etish" "Xonani tark etish" "Media va fayllar" diff --git a/features/roomdetails/impl/src/main/res/values-vi/translations.xml b/features/roomdetails/impl/src/main/res/values-vi/translations.xml index ac37e6fcfa..5e25014c88 100644 --- a/features/roomdetails/impl/src/main/res/values-vi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-vi/translations.xml @@ -46,6 +46,7 @@ "Không thể tắt tiếng phòng này, vui lòng thử lại." "Không thể bật tiếng cho phòng này. Vui lòng thử lại." "Mời ai đó" + "Mời" "Rời khỏi cuộc trò chuyện" "Rời phòng" "Tùy chỉnh" diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml index 1cbabe0e54..04ccc56adb 100644 --- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -56,6 +56,7 @@ "完成前請勿關閉應用程式。" "正在準備邀請……" "邀請夥伴" + "邀請" "離開對話" "離開聊天室" "媒體與檔案" diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index 267cdf5c04..1fef5583a3 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -56,6 +56,7 @@ "完成之前请勿关闭 app。" "正在准备邀请…" "邀请人员" + "邀请" "离开聊天" "离开房间" "媒体与文件" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 6275b2837e..d7092af70e 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -56,6 +56,7 @@ "Don\'t close the app until finished." "Preparing invitations…" "Invite people" + "Invite" "Leave conversation" "Leave room" "Media and files" diff --git a/features/roomdetailsedit/impl/src/main/res/values-pl/translations.xml b/features/roomdetailsedit/impl/src/main/res/values-pl/translations.xml index c676ff46ed..25be496204 100644 --- a/features/roomdetailsedit/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetailsedit/impl/src/main/res/values-pl/translations.xml @@ -1,6 +1,6 @@ - "Edytuj pokój" + "Edytuj szczegóły" "Wystąpił nieznany błąd i nie można było zmienić informacji." "Nie można zaktualizować pokoju" "Aktualizuję pokój…" diff --git a/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml b/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml index f17660f418..6ddb482189 100644 --- a/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-fa/translations.xml @@ -9,7 +9,7 @@ "در صورت دعوت می‌تواند دوباره به اتاق بپیوندد." "مطمئنید می‌خواهید این عضو را بردارید؟" "دیدن نمایه" - "برداشتن از اتاق" + "حذف کاربر" "برداشتن عضو و تحریم پیوستن در آینده؟" "برداشتن %1$s…" "تحریم نکردن از اتاق" diff --git a/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml b/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml index 20246af787..40ea1f099a 100644 --- a/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-pl/translations.xml @@ -4,12 +4,14 @@ "Zbanuj" "Nie będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni." "Czy na pewno chcesz zbanować tego członka?" + "Nie będą mogli ponownie dołączyć do tej przestrzeni, nawet jeśli zostaną zaproszeni, zachowają jednak członkostwo w pokojach lub podprzestrzeniach." "Banowanie %1$s" "Usuń" "Będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni." "Czy na pewno chcesz usunąć tego członka?" + "Będą mogli ponownie dołączyć do tej przestrzeni, jeśli zostaną zaproszeni, zachowując jednocześnie członkostwo w pokojach lub podprzestrzeniach." "Wyświetl profil" - "Usuń z pokoju" + "Usuń użytkownika" "Usunąć członka i zablokować możliwość dołączenia w przyszłości?" "Usuwanie %1$s…" "Odbanuj z pokoju" diff --git a/features/roommembermoderation/impl/src/main/res/values-uk/translations.xml b/features/roommembermoderation/impl/src/main/res/values-uk/translations.xml index cd6dd40e55..0ddc115f7d 100644 --- a/features/roommembermoderation/impl/src/main/res/values-uk/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-uk/translations.xml @@ -4,10 +4,12 @@ "Заблокувати" "Він не зможе приєднатися до цієї кімнати знову, якщо його запросять." "Ви точно хочете заблокувати цього користувача?" + "Вони не зможуть знову приєднатися до цього простору, навіть якщо їх запросять, але збережуть своє членство в будь-яких кімнатах або підпросторах." "Блокування %1$s" "Вилучити" "Вони зможуть знову приєднатися до цієї кімнати, якщо їх запросять." "Ви дійсно хочете вилучити цього учасника?" + "Вони зможуть знову приєднатися до цього простору, якщо їх запросять, і збережуть своє членство в будь-яких кімнатах або підпросторах." "Переглянути профіль" "Вилучити користувача" "Вилучити учасника та заборонити приєднання в майбутньому?" 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 ca9adbc5ac..0f4584271c 100644 --- a/features/securebackup/impl/src/main/res/values-el/translations.xml +++ b/features/securebackup/impl/src/main/res/values-el/translations.xml @@ -12,6 +12,7 @@ "Εισαγωγή κλειδιού ανάκτησης" "Ο αποθηκευτικός χώρος κλειδιών σου δεν είναι συγχρονισμένος αυτήν τη στιγμή." "Λήψη κλειδιού ανάκτησης" + "Οι συνομιλίες σας αποθηκεύονται αυτόματα με κρυπτογράφηση από άκρο σε άκρο. Για να επαναφέρετε αυτό το αντίγραφο ασφαλείας και να διατηρήσετε την ψηφιακή σας ταυτότητα όταν χάσετε την πρόσβαση σε όλες τις συσκευές σας, θα χρειαστείτε το κλειδί ανάκτησης. " "Άνοιγμα %1$s σε συσκευή υπολογιστή" "Συνδέσου ξανά στο λογαριασμό σου" "Όταν σου ζητηθεί να επαληθεύσεις τη συσκευή σου, επέλεξε %1$s" 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 987fd88190..c3d849f37b 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -61,9 +61,9 @@ "Seadista andmete taastamine" "Jah, lähtesta nüüd" "See tegevus on tagasipöördumatu." - "Kas sa oled kindel, et soovid oma andmete krüptimist lähtestada?" + "Kas sa oled kindel, et soovid oma võrguidentiteeti lähtestada?" "Tekkis teadmata viga. Palun kontrolli, kas sinu kasutajakonto salasõna on õige ja proovi uuesti." "Sisesta…" - "Palun kinnita, et soovid oma andmete krüptimist lähtestada." + "Palun kinnita, et soovid oma võrguidentiteedi lähtestada." "Jätkamaks sisesta oma kasutajakonto salasõna" 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 c16e5bd1d8..bd7a43ffba 100644 --- a/features/securebackup/impl/src/main/res/values-fa/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fa/translations.xml @@ -9,7 +9,7 @@ "تغییر کلید بازیابی" "ورود کلید بازیابی" "ذخیره‌ساز کلیدتان از هم‌گام بودن در آمده." - "برپایی بازیابی" + "دریافت کلید بازیابی" "گشودن %1$s در افزارهٔ میزکار" "ورود دوباره به حسابتان" "گزینش %1$s هنگام درخواست تأیید افزاره‌تان" @@ -23,10 +23,10 @@ "لازم است دوباره همهٔ آشنایان و افزاره‌های موجودتان را تأیید کنید" "فقط اگر به افزاره‌ای وارد شده از پیش دسترسی ندارید و کلید بازیابیتان را گم کرده‌اید بازنشانی کنید." "نمی‌توانید تأیید کنید؟ لازم است هویتتان را بازنشانی کنید." - "خاموش کردن" - "اگر از سیستم همه دستگاه ها خارج شده باشید، پیام های رمزگذاری شده خود را از دست خواهید داد." - "مطمئنید که می‌خواهید پشتیبان گیری را خاموش کنید؟" - "حذف فضای ذخیره سازی کلید، هویت رمزنگاری و کلیدهای پیام شما را از کارساز حذف می کند و ویژگی های امنیتی زیر را خاموش می کند:" + "حذف" + "اگر از تمام دستگاه‌هایتان خارج شوید، تاریخچه چت رمزگذاری‌شده خود را از دست خواهید داد و باید هویت دیجیتال خود را مجدداً تنظیم کنید." + "آیا مطمئن هستید که می‌خواهید کلید ذخیره‌سازی را حذف کنید؟" + "حذف محل ذخیره‌سازی کلید، کلیدهای هویت دیجیتال و پیام شما را از سرور حذف کرده و ویژگی‌های امنیتی زیر را غیرفعال می‌کند:" "سابقه پیام رمزگذاری شده در دستگاه های جدید نخواهید داشت" "اگر از %1$s در همه جا خارج شده باشید، دسترسی به پیام های رمزگذاری شده خود را از دست خواهید داد" "مطمئنید که می‌خواهید فضای ذخیره سازی کلید را خاموش کرده و آن را حذف کنید؟" @@ -56,7 +56,7 @@ "تولید کلید بازیابیتان" "با کسی هم‌رسانیش نکنید!" "برپایی بازیابی موفّق بود" - "برپایی بازیابی" + "دریافت کلید بازیابی" "بله. اکنون بازنشانی شود" "این فرایند بازگشت‌ناپذیر است." "ورود…" 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 fb30d786c0..f2f44e10f3 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -2,33 +2,34 @@ "Wyłącz backup" "Włącz 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" + "Umożliwi Ci to przeglądanie historii czatów na nowych urządzeniach i jest wymagane do tworzenia kopii zapasowych i tożsamości cyfrowej. %1$s." "Magazyn kluczy" - "Magazyn kluczy musi być włączony, aby włączyć przywracanie." + "Magazyn kluczy musi być włączony, aby włączyć archiwizowanie czatów." "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ń." + "Twoje czaty są automatycznie archiwizowane za pomocą szyfrowania end-to-end. Aby przywrócić tę kopię zapasową i swoją tożsamość cyfrową, wymagany będzie klucz przywracania." "Wprowadź klucz przywracania" "Magazyn kluczy nie jest zsynchronizowany." - "Skonfiguruj przywracanie" + "Uzyskaj klucz przywracania" + "Twoje czaty są automatycznie archiwizowane za pomocą szyfrowania end-to-end. Aby przywrócić tę kopię zapasową i swoją tożsamość cyfrową, wymagany będzie klucz przywracania." "Otwórz %1$s na urządzeniu stacjonarnym" "Zaloguj się ponownie na swoje konto" "Gdy pojawi się prośba o weryfikację urządzenia, wybierz %1$s" - "“Resetuj wszystko”" + "“Zresetuj wszystko”" "Postępuj zgodnie z instrukcjami, aby utworzyć nowy klucz przywracania" "Zapisz nowy klucz przywracania w menedżerze haseł lub notatce szyfrowanej" - "Resetuj szyfrowanie swojego konta za pomocą drugiego urządzenia" + "Zresetuj szyfrowanie swojego konta za pomocą drugiego urządzenia" "Kontynuuj resetowanie" "Szczegóły konta, kontakty, preferencje i lista czatów zostaną zachowane" "Utracisz istniejącą historię wiadomości" "Wymagana będzie ponowna weryfikacja istniejących urządzeń i kontaktów" - "Zresetuj swoją tożsamość tylko wtedy, gdy nie jesteś zalogowany na żadnym urządzeniu i straciłeś swój klucz przywracania." - "Zresetuj swoją tożsamość, jeśli nie możesz jej potwierdzić w inny sposób" - "Wyłącz" - "Jeśli wylogujesz się ze wszystkich urządzeń, stracisz wszystkie wiadomości szyfrowane." - "Czy na pewno chcesz wyłączyć backup?" - "Wyłączenie backupu spowoduje usunięcie kopii klucza szyfrowania i wyłączenie innych funkcji bezpieczeństwa. W takim przypadku będziesz:" + "Zresetuj swoją tożsamość tylko wtedy, gdy nie posiadasz dostępu do żadnego innego zweryfikowanego urządzenia i straciłeś swój klucz przywracania." + "Nie możesz potwierdzić? Zresetuj swoją tożsamość cyfrową." + "Usuń" + "Jeśli usuniesz wszystkie swoje urządzenia, stracisz zaszyfrowaną historię wiadomości i będziesz musiał zresetować swoją tożsamość cyfrową." + "Czy na pewno chcesz usunąć magazyn kluczy?" + "Usunięcie magazynu kluczy usunie Twoją tożsamość cyfrową i klucze wiadomości z serwera, wyłączając następujące funkcje bezpieczeństwa:" "Posiadał historii wiadomości szyfrowanych na nowych urządzeniach" "Utracisz dostęp do wiadomości szyfrowanych, jeśli zostaniesz wszędzie wylogowany z %1$s" "Czy na pewno chcesz wyłączyć backup?" @@ -58,12 +59,12 @@ "Wygeneruj klucz przywracania" "Nie udostępniaj tego nikomu!" "Skonfigurowano przywracanie pomyślnie" - "Skonfiguruj przywracanie" + "Uzyskaj klucz przywracania" "Tak, zresetuj teraz" "Tego procesu nie można odwrócić." - "Czy na pewno chcesz zresetować szyfrowanie?" + "Czy na pewno chcesz zresetować swoją tożsamość cyfrową?" "Wystąpił nieznany błąd. Sprawdź, czy hasło jest poprawne i spróbuj ponownie." "Wprowadź…" - "Potwierdź, że chcesz zresetować szyfrowanie." + "Potwierdź, że chcesz zresetować swoją tożsamość cyfrową." "Wprowadź hasło, aby kontynuować" 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 b02d5ecfbb..3e92a39df0 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -26,7 +26,7 @@ "Obnovte svoju totožnosť iba vtedy, ak nemáte prístup k inému prihlásenému zariadeniu a stratili ste kľúč na obnovenie." "Znovu nastavte svoju totožnosť v prípade, že ju nemôžete potvrdiť iným spôsobom" "Vypnúť" - "Stratíte prístup k svojim zašifrovaným správam, ak sa odhlásite zo všetkých zariadení" + "Ak odstránite všetky svoje zariadenia, stratíte svoju zašifrovanú históriu správ a budete si musieť obnoviť svoju digitálnu identitu." "Ste si istí, že chcete vypnúť zálohovanie?" "Vypnutím zálohovania sa odstráni aktuálna záloha šifrovacích kľúčov a vypnú sa ďalšie bezpečnostné funkcie. V tomto prípade:" "Na nových zariadeniach nebudete mať zašifrovanú históriu správ" 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 218e6d64fd..82a96fcab9 100644 --- a/features/securebackup/impl/src/main/res/values-uk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-uk/translations.xml @@ -12,6 +12,7 @@ "Введіть ключ відновлення" "Сховище ключів наразі не синхронізовано." "Налаштувати відновлення" + "Ваші чати автоматично резервуються з використанням наскрізного шифрування. Щоб відновити цю резервну копію та зберегти свою цифрову ідентичність у разі втрати доступу до всіх своїх пристроїв, вам знадобиться ключ відновлення." "Відкрийте %1$s на комп\'ютері" "Увійдіть до вашого облікового запису знову" "Коли вас попросять підтвердити пристрій, виберіть %1$s" diff --git a/features/securityandprivacy/impl/src/main/res/values-pl/translations.xml b/features/securityandprivacy/impl/src/main/res/values-pl/translations.xml index c62c4f2af4..60415c2288 100644 --- a/features/securityandprivacy/impl/src/main/res/values-pl/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-pl/translations.xml @@ -1,10 +1,17 @@ - "Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju." - "Adres pokoju" - "Dodaj adres pokoju" - "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić żądanie." + "Aby pokój był widoczny w katalogu pokoi publicznych, potrzebny jest adres pokoju." + "Edytuj adres" + "Przestrzenie, których członkowie mogą dołączyć do pokoju bez zaproszenia." + "Zarządzaj przestrzeniami" + "(Nieznana przestrzeń)" + "Inne przestrzenie, których nie jesteś członkiem" + "Twoje przestrzenie" + "Dodaj adres" + "Każdy w autoryzowanych przestrzeniach może dołączyć, ale wszyscy inni muszą poprosić o dostęp." + "Każdy musi poprosić o dostęp." "Poproś o dołączenie" + "Każdy w %1$s może dołączyć, ale wszyscy pozostali muszą poprosić o dostęp." "Tak, włącz szyfrowanie" "Po włączeniu szyfrowanie pokoju nie może zostać wyłączone, a historia wiadomości będzie widoczna tylko dla członków od momentu, w którym dołączyli lub zostali zaproszeni. Nikt poza członkami pokoju nie będzie mógł czytać wiadomości. Może to wpłynąć na prawidłowe działanie botów lub mostków. @@ -13,23 +20,31 @@ Odradzamy włączanie szyfrowania dla pokoi, które każdy może znaleźć i do "Po włączeniu szyfrowania nie można wyłączyć." "Szyfrowanie" "Włącz szyfrowanie end-to-end" - "Każdy może znaleźć i dołączyć" + "Każdy może dołączyć." "Każdy" - "Tylko osoby z zaproszeniem mogą dołączyć" - "Tylko zaproszenie" - "Dostęp do pokoju" + "Wybierz, którzy członkowie przestrzeni mogą dołączyć do tego pokoju bez zaproszenia. %1$s" + "Zarządzaj przestrzeniami" + "Tylko zaproszone osoby mogą dołączyć" + "Tylko na zaproszenie" + "Dostęp" + "Każdy w autoryzowanych przestrzeniach może dołączyć." + "Każdy w %1$s może dołączyć." + "Członkowie przestrzeni" "Przestrzenie nie są obecnie wspierane" - "Aby pokój był widoczny w katalogu, potrzebny jest adres pokoju." - "Adres pokoju" + "Aby pokój był widoczny w katalogu pokoi publicznych, potrzebny jest adres pokoju." + "Adres" "Zezwól na znalezienie tego pokoju wyszukując %1$s w katalogu pokoi publicznych" + "Zezwól, by inni mogli Cię znaleźć, przeszukując katalog publiczny." "Widoczny w katalogu pokoi publicznych" - "Ktokolwiek" + "Każdy (historia jest publiczna)" + "Zmiany nie zmienią przeszłych wiadomości, tylko nowe. %1$s" "Kto może czytać historię" - "Od momentu kiedy członkowie zostali zaproszeni" - "Członkowie od momentu włączenia tej opcji" + "Członkowie od kiedy zostali zaproszeni" + "Członkowie (cała historia)" "Adresy pokoju umożliwiają łatwe znalezienie i dołączenie do pokojów. Również możesz się zdecydować na upublicznienie Twojego serwera w katalogu pokoi publicznych." "Publikowanie pokoju" - "Widoczność pokoju" + "Adresy pokoi pomagają w znalezieniu i dołączeniu do pokoi i przestrzeni. Umożliwiają również łatwe udostępnianie ich innym." + "Widoczność" "Bezpieczeństwo i prywatność" diff --git a/features/securityandprivacy/impl/src/main/res/values-uk/translations.xml b/features/securityandprivacy/impl/src/main/res/values-uk/translations.xml index f8b96ee6c5..0921887ccb 100644 --- a/features/securityandprivacy/impl/src/main/res/values-uk/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-uk/translations.xml @@ -8,8 +8,10 @@ "Інші простори, учасником яких ви не є" "Ваші простори" "Додати адресу" + "Будь-хто в авторизованих просторах може приєднатися, але всі інші повинні подати запит на доступ." "Усі повинні запитувати доступ." "Запит на приєднання" + "Будь-хто з %1$s може приєднатися, але всі інші повинні подати запит на доступ." "Так, увімкнути шифрування" "Після ввімкнення шифрування кімнати, його неможливо вимкнути, історію повідомлень бачитимуть лише учасники кімнати, яких було запрошено або які приєдналися до кімнати. Ніхто, крім учасників кімнати, не зможе прочитати повідомлення. Це може перешкоджати коректній роботі ботів і мостів. @@ -25,11 +27,14 @@ "Приєднатися можуть лише запрошені люди." "Лише запрошені" "Доступ" + "Долучитися може будь-хто, хто має доступ до авторизованих просторів." "Долучитися може будь-хто з %1$s." + "Учасники простору" "Простори наразі не підтримуються" "Вам знадобиться адреса кімнати, щоб зробити її видимою в каталозі." "Адреса" "Дозвольте, щоб цю кімнату можна було знайти за допомогою пошуку в каталозі загальнодоступних кімнат %1$s " + "Дозвольте знаходити вас за допомогою пошуку в публічному каталозі." "Видима в загальному каталозі" "Будь-хто (загальнодоступна історія)" "Зміни не вплинуть на попередні повідомлення, лише на нові. %1$s" @@ -39,6 +44,7 @@ "Адреси кімнат — це спосіб знайти кімнату та отримати до неї доступ. Це також гарантує, що ви можете легко поділитися своєю кімнатою з іншими. Ви можете опублікувати свою кімнату в каталозі загальнодоступних кімнат вашого домашнього сервера." "Публікація в кімнаті" + "Адреси — це спосіб знаходити кімнати та простори та отримувати до них доступ. Це також гарантує, що ви зможете легко ділитися ними з іншими." "Видимість" "Безпека й приватність" diff --git a/features/space/impl/src/main/res/values-fa/translations.xml b/features/space/impl/src/main/res/values-fa/translations.xml index dcab5c912a..d8f377c0b2 100644 --- a/features/space/impl/src/main/res/values-fa/translations.xml +++ b/features/space/impl/src/main/res/values-fa/translations.xml @@ -8,6 +8,6 @@ "تنها مدیر %1$s هستید" "دیدن اعضا" "ترک فضا" - "نقش‌ها و اجازه‌ها" + "نقش‌ها و مجوزها" "امنیت و محرمانگی" diff --git a/features/space/impl/src/main/res/values-pl/translations.xml b/features/space/impl/src/main/res/values-pl/translations.xml index f1a964c6e3..9c3097b75f 100644 --- a/features/space/impl/src/main/res/values-pl/translations.xml +++ b/features/space/impl/src/main/res/values-pl/translations.xml @@ -9,9 +9,21 @@ "Wybierz pokoje, które chcesz opuścić, a których nie jesteś jedynym administratorem:" "Aby opuścić tę przestrzeń, musisz przypisać do niej innego administratora." + "Jesteś jedynym właścicielem %1$s. Musisz przenieść własność zanim odejdziesz." "Nie zostaniesz usunięty z następujących pokoi, ponieważ jesteś ich jedynym administratorem:" "Opuścić %1$s?" "Jesteś jedynym administratorem %1$s" + "Przenieś własność" + "Pokój" + "Dodanie pokoju nie wpłynie na dostęp do niego. Aby zmienić ustawienia dostępu, przejdź do Ustawienia pokoju > Bezpieczeństwo i prywatność." + "Dodaj swój pierwszy pokój" + "Zobacz członków" + "Usunięcie pokoju nie wpłynie na dostęp do niego. Aby zmienić ustawienia dostępu, przejdź do Ustawienia pokoju > Bezpieczeństwo i prywatność." + + "Usuń %1$d pokój z %2$s" + "Usuń %1$d pokoje z %2$s" + "Usuń %1$d pokoi z %2$s" + "Opuść przestrzeń" "Role i uprawnienia" "Bezpieczeństwo i prywatność" diff --git a/features/space/impl/src/main/res/values-uk/translations.xml b/features/space/impl/src/main/res/values-uk/translations.xml index e124f72826..df4b4fb922 100644 --- a/features/space/impl/src/main/res/values-uk/translations.xml +++ b/features/space/impl/src/main/res/values-uk/translations.xml @@ -2,14 +2,28 @@ "Оберіть власників" "%1$s (Адміністратор)" + + "Залишити %1$d кімнату та простір" + "Залишити %1$d кімнату та простори" + "Залишити %1$d кімнат та просторів" + "Виберіть кімнати, з яких ви хочете вийти, і в них ви не єдиний адміністратор:" "Перш ніж ви зможете вийти, вам потрібно призначити іншого адміністратора для цього простору." + "Ви єдиний власник %1$s. Перш ніж піти, вам потрібно передати право володіння комусь іншому." "Вас не буде видалено з цих кімнат, оскільки ви єдиний адміністратор:" "Вийти з %1$s?" "Ви єдиний адміністратор у %1$s" + "Передача права володіння" "Кімната" + "Додавання кімнати не вплине на доступ до неї. Щоб змінити доступ, перейдіть до «Налаштування кімнати» > «Безпека та конфіденційність»." "Додайте свою першу кімнату" "Переглянути учасників" + "Видалення кімнати не вплине на доступ до неї. Щоб змінити доступ, перейдіть до розділу «Налаштування кімнати» > «Безпека та конфіденційність»." + + "Видалити %1$d кімнату з %2$s" + "Видалити %1$d кімнати з %2$s" + "Видалити %1$d кімнат з %2$s" + "Вийти з простору" "Ролі та дозволи" "Безпека й приватність" diff --git a/features/verifysession/impl/src/main/res/values-fa/translations.xml b/features/verifysession/impl/src/main/res/values-fa/translations.xml index aa902a94e1..3447c07a7d 100644 --- a/features/verifysession/impl/src/main/res/values-fa/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fa/translations.xml @@ -13,6 +13,7 @@ "يه چيزي درست به نظر نمياد یا زمان درخواست به پایان رسید یا درخواست رد شد." "تأیید تطابق شکلک‌های زیر با شکلک‌های نشان داده شده روی افزارهٔ دیگرتان." "مقایسهٔ شکلک‌ها" + "تأیید کنید که اعداد زیر با اعداد نشان داده شده در جلسه دیگر شما مطابقت دارند." "مقایسهٔ اعداد" "اکنون می‌توانید روی افزارهٔ دیگرتان با امنیت پیام فرستاده و بخوانید." "افزاره تأیید شده" 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 92b0572d08..39ce9f6fa3 100644 --- a/features/verifysession/impl/src/main/res/values-pl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pl/translations.xml @@ -2,8 +2,8 @@ "Nie możesz potwierdzić?" "Utwórz nowy klucz przywracania" - "Zweryfikuj to urządzenie, aby skonfigurować bezpieczne przesyłanie wiadomości." - "Potwierdź, że to Ty" + "Wybierz sposób weryfikacji, aby skonfigurować bezpieczne wiadomości." + "Potwierdź swoją tożsamość cyfrową" "Użyj innego urządzenia" "Użyj klucza przywracania" "Teraz możesz bezpiecznie czytać i wysyłać wiadomości, każdy z kim czatujesz również może ufać temu urządzeniu." @@ -50,5 +50,5 @@ "Po zaakceptowaniu będziesz mógł kontynuować weryfikację." "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować." "Oczekiwanie na zaakceptowanie prośby" - "Wylogowywanie…" + "Usuwam urządzenie…" diff --git a/features/verifysession/impl/src/main/res/values-sk/translations.xml b/features/verifysession/impl/src/main/res/values-sk/translations.xml index 932b8555fc..d4e6f60402 100644 --- a/features/verifysession/impl/src/main/res/values-sk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml @@ -50,5 +50,5 @@ "Po prijatí budete môcť pokračovať v overovaní." "Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii." "Čaká sa na prijatie žiadosti" - "Prebieha odhlasovanie…" + "Odoberanie zariadenia…" diff --git a/features/verifysession/impl/src/main/res/values-uk/translations.xml b/features/verifysession/impl/src/main/res/values-uk/translations.xml index 1967fef383..03fe318d13 100644 --- a/features/verifysession/impl/src/main/res/values-uk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uk/translations.xml @@ -50,5 +50,5 @@ "Після погодження ви зможете продовжити верифікацію." "Щоб продовжити, прийміть запит на початок процесу верифікації в іншому сеансі." "Очікування на прийняття запиту" - "Вихід…" + "Видалення пристрою…" diff --git a/libraries/matrixui/src/main/res/values-it/translations.xml b/libraries/matrixui/src/main/res/values-it/translations.xml index 439d61337e..913c45867c 100644 --- a/libraries/matrixui/src/main/res/values-it/translations.xml +++ b/libraries/matrixui/src/main/res/values-it/translations.xml @@ -3,5 +3,7 @@ "Invia invito" "Vuoi iniziare una conversazione con%1$s?" "Inviare invito?" + "Al momento non hai alcuna conversazione con questa persona. Conferma l\'invito prima di continuare." + "Vuoi avviare una conversazione con questo nuovo contatto?" "%1$s (%2$s) ti ha invitato" diff --git a/libraries/matrixui/src/main/res/values-pl/translations.xml b/libraries/matrixui/src/main/res/values-pl/translations.xml index caa2c0e07d..bd31d7edb4 100644 --- a/libraries/matrixui/src/main/res/values-pl/translations.xml +++ b/libraries/matrixui/src/main/res/values-pl/translations.xml @@ -3,5 +3,7 @@ "Wyślij zaproszenie" "Czy chcesz rozpocząć czat z %1$s?" "Wysłać zaproszenie?" + "Obecnie nie posiadasz żadnych czatów z tą osobą. Potwierdź zaproszenie, zanim przejdziesz dalej." + "Rozpocząć czat z nowym kontaktem?" "%1$s (%2$s) zaprosił Cię" diff --git a/libraries/matrixui/src/main/res/values-uk/translations.xml b/libraries/matrixui/src/main/res/values-uk/translations.xml index 324bb96086..4d2063fbd2 100644 --- a/libraries/matrixui/src/main/res/values-uk/translations.xml +++ b/libraries/matrixui/src/main/res/values-uk/translations.xml @@ -3,5 +3,7 @@ "Надіслати запрошення" "Хочете розпочати бесіду з %1$s?" "Надіслати запрошення?" + "Наразі у вас немає чатів із цим користувачем. Підтвердьте запрошення, перш ніж продовжити." + "Розпочати чат із цим новим контактом?" "%1$s (%2$s) запрошує вас" diff --git a/libraries/mediaviewer/impl/src/main/res/values-it/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-it/translations.xml index 72701194fc..913329122e 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-it/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-it/translations.xml @@ -16,6 +16,7 @@ "Nome del file" "Nessun altro file da mostrare" "Non ci sono più contenuti multimediali da mostrare" + "Informazioni sul file" "Caricato da" "Caricato il" diff --git a/libraries/mediaviewer/impl/src/main/res/values-pl/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-pl/translations.xml index c398123873..a1f7a3bdf9 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-pl/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-pl/translations.xml @@ -16,6 +16,7 @@ "Nazwa pliku" "Brak plików do pokazania" "Brak mediów do pokazania" + "Informacje pliku" "Przesłane przez" "Przesłane w dniu" diff --git a/libraries/mediaviewer/impl/src/main/res/values-uk/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-uk/translations.xml index 45e1e1703b..ddc316fb4b 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-uk/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-uk/translations.xml @@ -16,6 +16,7 @@ "Назва файлу" "Більше немає файлів для показу" "Більше немає медіа для показу" + "Інформація про файл" "Вивантажено користувачем" "Вивантажено" diff --git a/libraries/push/impl/src/main/res/values-pl/translations.xml b/libraries/push/impl/src/main/res/values-pl/translations.xml index b9ae289507..d8a4077d05 100644 --- a/libraries/push/impl/src/main/res/values-pl/translations.xml +++ b/libraries/push/impl/src/main/res/values-pl/translations.xml @@ -15,7 +15,14 @@ "%d powiadomienia" "%d powiadomień" + "Nie udało się zarejestrować dystrybutora powiadomień Push, przez co nie będziesz już otrzymywać powiadomień. Sprawdź ustawienia powiadomień aplikacji i status dystrybutora powiadomień Push." "Masz nowe wiadomości." + + "Masz %d nową wiadomość." + "Masz %d nowe wiadomości." + "Masz %d nowych wiadomości." + + "📞 Połączenie przychodzące" "📹 Połączenie przychodzące" "** Nie udało się wysłać - proszę otworzyć pokój" "Dołącz" @@ -41,12 +48,15 @@ "%1$s zaprosił Cię do pokoju" "Ja" "%1$s wspomniał lub odpowiedział" + "Zaprosił Cię do dołączenia do przestrzeni" + "%1$s zaprosił Cię do dołączenia do przestrzeni" "Wyświetlasz powiadomienie! Kliknij mnie!" + "Wątek w %1$s" "%1$s: %2$s" "%1$s: %2$s %3$s" "%d nieprzeczytana wiadomość" - "%d nieprzeczytane wiadomość" + "%d nieprzeczytane wiadomości" "%d nieprzeczytanych wiadomości" "%1$s i %2$s" diff --git a/libraries/push/impl/src/main/res/values-uk/translations.xml b/libraries/push/impl/src/main/res/values-uk/translations.xml index d3aee22165..9c5f74605f 100644 --- a/libraries/push/impl/src/main/res/values-uk/translations.xml +++ b/libraries/push/impl/src/main/res/values-uk/translations.xml @@ -15,7 +15,14 @@ "%d сповіщення" "%d сповіщень" + "Розподільник сповіщень UnifiedPush не вдалося зареєструвати, тому ви більше не отримуватимете сповіщень. Перевірте налаштування сповіщень у додатку та статус розподільника push-сповіщень." "У вас є нові повідомлення." + + "У вас %d нове повідомлення." + "У вас %d нових повідомлення." + "У вас %d нових повідомлень." + + "📞 Вхідний дзвінок" "📹 Вхідний виклик" "** Не вдалося надіслати - відкрийте кімнату" "Доєднатися" @@ -41,6 +48,7 @@ "%1$s запросив вас приєднатися до кімнати" "Я" "%1$s згадували або відповідали" + "Вас запросили приєднатися до простору" "%1$s запрошує вас приєднатися до простору" "Ви переглядаєте сповіщення! Натисніть тут!" "Гілка в %1$s" @@ -62,11 +70,18 @@ "Фонова синхронізація" "Сервіси Google" "Не знайдено дійсних сервісів Google Play. Сповіщення можуть не працювати належним чином." + "Перевірка заблокованих користувачів" "Переглянути заблокованих користувачів" "Немає заблокованих користувачів" + + "Ви заблокували %1$d користувача. Ви не отримуватимете сповіщення для цього користувача." + "Ви заблокували %1$d користувачів. Ви не отримуватимете сповіщення для цих користувачів." + "Ви заблокували %1$d користувачів. Ви не отримуватимете сповіщення для цих користувачів." + "Заблоковані користувачі" "Отримує назву поточного постачальника." "Постачальників push-сповіщень не вибрано." + "Поточний провайдер push-повідомлень: %1$s та поточний дистриб\'ютор: %2$s. Але дистриб\'ютор %3$s не знайдено. Можливо, додаток було видалено?" "Поточний постачальник push-сповіщень: %1$s, але дистриб\'юторів не налаштовано." "Поточний постачальник: %1$s." "Поточний постачальник push-сповіщень: %1$s (%2$s)" 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 4011474193..f106b46e6c 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -244,7 +244,6 @@ "Ключ аднаўлення" "Абнаўленне…" "Адказвае на %1$s" - "Паведаміць пра памылку" "Паведаміць аб праблеме" "Скарга прынята" "Рэдактар фарматаванага тэксту" diff --git a/libraries/ui-strings/src/main/res/values-bg/translations.xml b/libraries/ui-strings/src/main/res/values-bg/translations.xml index 94e050727d..596b7b3e34 100644 --- a/libraries/ui-strings/src/main/res/values-bg/translations.xml +++ b/libraries/ui-strings/src/main/res/values-bg/translations.xml @@ -231,7 +231,6 @@ "%1$d отговора" "В отговор на %1$s" - "Съобщаване за грешка" "Съобщаване за проблем" "Докладът е изпратен" "Редактор на богат текст" 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 4343f2980b..e177c23957 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -324,7 +324,6 @@ Důvod: %1$s." "%1$d odpovědí" "Odpověď na %1$s" - "Nahlásit chybu" "Nahlásit problém" "Zpráva odeslána" "Editor formátovaného textu" diff --git a/libraries/ui-strings/src/main/res/values-cy/translations.xml b/libraries/ui-strings/src/main/res/values-cy/translations.xml index 9c4b04b9d5..98538f517d 100644 --- a/libraries/ui-strings/src/main/res/values-cy/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cy/translations.xml @@ -309,7 +309,6 @@ Rheswm: %1$s." "%1$d ateb" "Yn ymateb i %1$s" - "Adrodd ar wall" "Adrodd am broblem" "Adroddiad wedi ei gyflwyno" "Golygydd testun cyfoethog" diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index f1ff7ec747..1769a5d223 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -323,7 +323,6 @@ "%1$d svar" "Svarer til %1$s" - "Rapportér en fejl" "Anmeld et problem" "Anmeldelsen er indsendt" "Rich text editor" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 739d003f88..e6332b9df2 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -316,7 +316,6 @@ Grund: %1$s." "%1$d Antworten" "%1$s antworten" - "Einen Fehler melden" "Ein Problem melden" "Bericht eingereicht" "Rich-Text-Editor" 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 beb97c54d1..a12122b00b 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -120,6 +120,7 @@ "Αποχώρηση από τον χώρο" "Φόρτωσε περισσότερα" "Διαχείριση λογαριασμού" + "Διαχείριση λογαριασμού και συσκευών" "Διαχείριση συσκευών" "Διαχείριση αίθουσών" "Στείλε" @@ -170,6 +171,7 @@ "Ξανά από την αρχή" "Έναρξη επαλήθευσης" "Πάτα για φόρτωση χάρτη" + "Διακοπή" "Τράβηξε φωτογραφία" "Πάτα για επιλογές" "Μετάφραση" @@ -224,6 +226,7 @@ "Κενό αρχείο" "Κρυπτογράφηση" "Η κρυπτογράφηση ενεργοποιήθηκε" + "Τελειώνει στις %1$s" "Εισήγαγε το PIN σου" "Σφάλμα" "Παρουσιάστηκε σφάλμα, ενδέχεται να μην λαμβάνεις ειδοποιήσεις για νέα μηνύματα. Αντιμετώπισε το πρόβλημα με τις ειδοποιήσεις από τις ρυθμίσεις. @@ -250,6 +253,8 @@ "Η γραμμή αντιγράφηκε στο πρόχειρο" "Ο σύνδεσμος αντιγράφηκε στο πρόχειρο" "Σύνδεση νέας συσκευής" + "Ζωντανή τοποθεσία" + "Η ζωντανή τοποθεσία έληξε" "Φόρτωση…" "Φόρτωση περισσότερων…" @@ -311,7 +316,6 @@ "%1$d απαντήσεις" "Απάντηση σε %1$s" - "Αναφορά σφάλματος" "Αναφορά προβλήματος" "Η αναφορά υποβλήθηκε" "Επεξεργαστής εμπλουτισμένου κειμένου" @@ -346,6 +350,7 @@ "Ρυθμίσεις" "Κοινή χρήση χώρου" "Τα νέα μέλη βλέπουν το ιστορικό" + "Κοινόχρηστη ζωντανή τοποθεσία" "Κοινόχρηστη τοποθεσία" "Κοινόχρηστος χώρος" "Αφαίρεση συσκευής" @@ -368,6 +373,7 @@ "Κείμενο" "Ειδοποιήσεις τρίτων" "Νήμα" + "Νήματα" "Θέμα" "Τι αφορά αυτή η αίθουσα;" "Δεν είναι δυνατή η αποκρυπτογράφηση" @@ -398,6 +404,7 @@ "Φωνητικό μήνυμα" "Αναμονή…" "Αναμονή για αυτό το μήνυμα" + "Αναμονή για ζωντανή τοποθεσία…" "Οποιοσδήποτε μπορεί να δει το ιστορικό" "Εσύ" "%1$s (%2$s) μοιράστηκε αυτό το μήνυμα, καθώς δεν ήσασταν στην αίθουσα όταν στάλθηκε." diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index fa0bc700cb..1dc69b127c 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -251,7 +251,6 @@ Motivo: %1$s." "Clave de recuperación" "Recargando…" "Respondiendo a %1$s" - "Informar de un error" "Informar de un problema" "Informe enviado" "Editor de texto enriquecido" 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 beedf175bd..e76e3153e1 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -69,6 +69,7 @@ "Helista" "Loobu" "Hetkel jäta tegemata" + "Vali fail" "Vali foto" "Selge" "Sulge" @@ -88,12 +89,15 @@ "Eemalda konto kasutusest" "Keeldu" "Keeldu ja blokeeri" + "Kustuta" + "Kustuta kasutajakonto" "Kustuta küsitlus" "Eemalda kõik valikud" "Lülita välja" "Loobu" "Lõpeta" "Valmis" + "Laadi alla" "Muuda" "Muuda selgitust" "Muuda küsitlust" @@ -201,7 +205,9 @@ "Beetaversioon" "Blokeeritud kasutajad" "Mullid" + "Osapool keeldus kõnest" "Kõne algas" + "Sa keeldusid kõnest" "Vestluse varukoopia" "Kopeeritud lõikelauale" "Autoriõigused" @@ -253,6 +259,8 @@ Põhjus: %1$s." "Rida on kopeeritud lõikelauale" "Link on kopeeritud lõikelauale" "Seo uus seade" + "Asukoha jagamine reaalajas" + "Reaalajas asukoha jagamine on lõppenud" "Laadime…" "Laadime veel…" @@ -279,6 +287,7 @@ Põhjus: %1$s." "Võrgust väljas" "Avatud lähtekoodiga litsentsid" "või" + "Muud valikud" "Salasõna" "Inimesed" "Püsilink" @@ -313,7 +322,6 @@ Põhjus: %1$s." "%1$d vastust" "Vastates kasutajale %1$s" - "Teata veast" "Teata veast" "Veateade on saadetud" "Vormindatud teksti toimeti" @@ -401,6 +409,7 @@ Põhjus: %1$s." "Häälsõnum" "Ootame…" "Ootame selle sõnumi dekrüptimisvõtit" + "Ootan asukoha jagamist reaalajas…" "Kõik võivad ajalugu näha" "Sina" "Kuna sind polnud saatmise ajal jututoas, siis %1$s (%2$s) jagas seda sõnumit sinuga." @@ -461,6 +470,11 @@ Kas sa oled kindel, et soovid jätkata?" "Seadistused" "Mitte keegi ei jaga oma asukohta" "Asukoht on jagamisel reaalajas" + + "%1$d isik" + "%1$d isikut" + + "Kaardil" "Meediafaili valimine ei õnnestunud. Palun proovi uuesti." "Siia lisamiseks vajuta sõnumil ja vali „%1$s“." "Et olulisi sõnumeid oleks lihtsam leida, tõsta nad esile" @@ -485,6 +499,7 @@ Kas sa oled kindel, et soovid jätkata?" "Sõnum jututoas %1$s" "Näita rohkem" "Näita vähem" + "Asukoht on jagamisel reaalajas" "Sa juba vaatad seda jututuba!" "%1$s / %2$s" "%1$s esiletõstetud sõnumit" diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml index bef5f1a4b5..7e9d3a05fe 100644 --- a/libraries/ui-strings/src/main/res/values-eu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -256,7 +256,6 @@ Arrazoia: %1$s." "Berreskuratze-gakoa" "Freskatzen…" "%1$s(r)i erantzuten" - "Eman akats baten berri" "Eman arazo baten berri" "Salaketa bidali da" "Testu aberatsaren editorea" diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index 872493f7de..418ca96419 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -146,7 +146,7 @@ "نمایش" "ورود دوباره" "برداشتن این افزاره" - "خروج به هر صورت" + "به هر حال این دستگاه را حذف کنید" "پرش" "آغاز" "آغاز گپ" @@ -225,6 +225,10 @@ "پیوند در تخته‌گیره رونوشت شد" "بار کردن…" "بار کردن بیش‌تر…" + + "%d دیگر" + "%d دیگران" + "%1$d عضو" "%1$d عضو" @@ -270,7 +274,6 @@ "کلید بازیابی" "تازه سازی…" "پاسخ دادن به %1$s" - "گزارش یک اشکال" "گزارش مشکل" "گزارش ثبت شد" "ویرایشگر متن غنی" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 48601352ba..e35ef5c2e1 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -317,7 +317,6 @@ Syy: %1$s." "%1$d vastausta" "Vastataan käyttäjälle %1$s" - "Ilmoita virheestä" "Ilmoita ongelmasta" "Ilmoitus lähetetty" "Rikastettu tekstieditori" 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 7e0c4e9481..7617571f17 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -324,7 +324,6 @@ Raison : %1$s." "%1$d réponses" "En réponse à %1$s" - "Signaler un problème" "Remonter un problème" "Rapport soumis" "Éditeur de texte enrichi" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index 2382eafc1a..7c3cfbd83b 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -330,7 +330,6 @@ Razlog: %1$s ." "%1$d odgovora" "Odgovara korisniku %1$s" - "Prijavi pogrešku" "Prijavi problem" "Prijava je podnesena" "Uređivač obogaćenog teksta" 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 f6498f228f..8973ebc4f6 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -71,6 +71,7 @@ "Hívás" "Mégse" "Egyelőre nem" + "Fájl kiválasztása" "Fénykép kiválasztása" "Törlés" "Bezárás" @@ -323,7 +324,6 @@ Ok: %1$s." "%1$d válasz" "Válasz %1$s számára" - "Hiba jelentése" "Probléma jelentése" "A jelentés elküldve" "Formázott szöveges szerkesztő" @@ -466,6 +466,9 @@ Biztos, hogy folytatja?" "Elnézést, hiba történt" "🔐️ Csatlakozz hozzám itt: %1$s" "Beszélgessünk itt: %1$s, %2$s" + "Élő földrajzi hely megosztása" + "Helymegosztás folyamatban" + "Élő hely: %1$s" "%1$s Android" "Az eszköz rázása a hibajelentéshez" "Képernyőkép" 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 77e405e1c8..ec5a582838 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -275,7 +275,6 @@ Alasan: %1$s." "%1$d balasan" "Membalas %1$s" - "Laporkan kutu" "Laporkan masalah" "Laporan terkirim" "Penyunting teks kaya" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 824b9f6441..ba0d6f022c 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -14,6 +14,7 @@ "Dettagli sulla crittografia" "Allarga il campo di testo del messaggio" "Nascondi password" + "Info" "Entra in chiamata" "Vai alla fine" "Sposta la mappa sulla mia posizione" @@ -48,6 +49,7 @@ "Invia file" "Posizione del mittente" "Azione richiesta a tempo limitato, hai un minuto per la verifica" + "Impostazioni, azione richiesta" "Mostra password" "Avvia una chiamata" "Avvia una videochiamata" @@ -69,6 +71,7 @@ "Chiama" "Annulla" "Annulla per ora" + "Scegli il file" "Scegli foto" "Cancella" "Chiudi" @@ -88,12 +91,15 @@ "Disattiva account" "Rifiuta" "Rifiuta e blocca" + "Elimina" + "Elimina account" "Elimina sondaggio" "Deseleziona tutti" "Disabilita" "Annulla" "Chiudi" "Fine" + "Scarica" "Modifica" "Modifica didascalia" "Modifica sondaggio" @@ -201,7 +207,9 @@ "Beta" "Utenti bloccati" "Fumetti" + "Chiamata rifiutata" "Chiamata avviata" + "Hai rifiutato una chiamata" "Backup della chat" "Copiato negli appunti" "Copyright" @@ -317,7 +325,6 @@ Motivo:. %1$s" "%1$d risposte" "Risposta a %1$s" - "Segnala un problema" "Segnala un problema" "Segnalazione inviata" "Editor di testo avanzato" @@ -460,6 +467,8 @@ Sei sicuro di voler continuare?" "Siamo spiacenti, si è verificato un errore" "🔐️ Unisciti a me su %1$s" "Ehi, parliamo su %1$s: %2$s" + "Condivisione della posizione in corso" + "%1$s Posizione in tempo reale" "%1$s Android" "Scuoti per segnalare un problema" "Istantanea schermo" @@ -467,6 +476,13 @@ Sei sicuro di voler continuare?" "Risposte" "Rimuovi %1$s" "Impostazioni" + "Nessuno sta condividendo la propria posizione" + "Condivisione posizione in tempo reale" + + "%1$d persona" + "%1$d persone" + + "Sulla mappa" "Selezione del file multimediale fallita, riprova." "Premi su un messaggio e scegli “%1$s” per includerlo qui." "Fissa i messaggi importanti così che possano essere trovati facilmente" @@ -491,6 +507,7 @@ Sei sicuro di voler continuare?" "Messaggio in %1$s" "Espandi" "Riduci" + "Condivisione posizione in tempo reale" "Stai già visualizzando questa stanza!" "%1$s di %2$s" "%1$s Messaggi fissati" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index 1c022f850f..b7a33d4dfd 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -319,7 +319,6 @@ "%1$d 件の返信" "%1$s に返信" - "バグを報告" "問題を報告" "報告は送信されました" "リッチテキストエディター" diff --git a/libraries/ui-strings/src/main/res/values-ka/translations.xml b/libraries/ui-strings/src/main/res/values-ka/translations.xml index b7a8819391..aa7659dde2 100644 --- a/libraries/ui-strings/src/main/res/values-ka/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ka/translations.xml @@ -186,7 +186,6 @@ "განახლება…" "პასუხი %1$s-ს" - "ხარვეზის შეტყობინება" "შეტყობინება პრობლემაზე" "რეპორტი გაგზავნილია" "მდიდარი ტექსტის რედაქტორი" diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 6a87870658..4f594f002b 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -310,7 +310,6 @@ "%1$d 답변" "%1$s님에게 답장하는 중" - "버그 보고" "문제 보고" "보고 제출됨" "리치 텍스트 편집기" diff --git a/libraries/ui-strings/src/main/res/values-lt/translations.xml b/libraries/ui-strings/src/main/res/values-lt/translations.xml index 280209f6a5..da5281212e 100644 --- a/libraries/ui-strings/src/main/res/values-lt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-lt/translations.xml @@ -126,7 +126,6 @@ "Reakcijos" "Atnaujinama…" "Atsakant %1$s" - "Pranešti apie klaidą" "Skundas pateiktas" "Kambario pavadinimas" "pvz., jūsų projekto pavadinimas" diff --git a/libraries/ui-strings/src/main/res/values-nb/translations.xml b/libraries/ui-strings/src/main/res/values-nb/translations.xml index fc641df34d..a0bb1b1a16 100644 --- a/libraries/ui-strings/src/main/res/values-nb/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nb/translations.xml @@ -309,7 +309,6 @@ "%1$d svar" "Svar til %1$s" - "Rapporter en feil" "Rapporter et problem" "Rapport sendt inn" "Redigeringsprogram for rik tekst" 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 6e136e7d77..ab39365f8e 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -250,7 +250,6 @@ Reden: %1$s." "%1$d antwoorden" "Reageren op %1$s" - "Een fout melden" "Meld een probleem" "Melding ingediend" "Uitgebreide tekstverwerker" 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 6812885c9c..30f2c383b6 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -15,6 +15,7 @@ "Szczegóły szyfrowania" "Powiększ pole tekstowe wiadomości" "Ukryj hasło" + "Informacje" "Dołącz do połączenia" "Przejdź na dół" "Przesuń mapę do mojej lokalizacji" @@ -30,8 +31,10 @@ "Pole PIN" "Przypięta lokalizacja" "Odtwórz" + "Prędkość odtwarzania" "Ankieta" "Zakończona ankieta" + "Kod QR" "Zareaguj z %1$s" "Zareaguj innym emoji" "Odczytane przez %1$s i %2$s" @@ -46,9 +49,13 @@ "Usuń reakcję z %1$s" "Awatar pokoju" "Wyślij pliki" + "Lokalizacja nadawcy" "Wymagane jest działanie ograniczone czasowo, została jedna minuta" + "Ustawienia, wymagane działanie" "Pokaż hasło" "Rozpocznij rozmowę" + "Rozpocznij rozmowę wideo" + "Rozpocznij połączenie głosowe" "Pokój nagrobkowy" "Awatar użytkownika" "Menu użytkownika" @@ -60,11 +67,13 @@ "Twój awatar" "Akceptuj" "Dodaj opis" + "Dodaj istniejące pokoje" "Dodaj do osi czasu" "Wróć" "Zadzwoń" "Anuluj" "Anuluj na razie" + "Wybierz plik" "Wybierz zdjęcie" "Wyczyść" "Zamknij" @@ -79,26 +88,32 @@ "Kopiuj tekst" "Utwórz" "Utwórz pokój" + "Utwórz przestrzeń" "Dezaktywuj" "Dezaktywuj konto" "Odrzuć" "Odrzuć i zablokuj" + "Usuń" + "Usuń konto" "Usuń ankietę" "Odznacz wszystko" "Wyłącz" "Odrzuć" "Zamknij" "Gotowe" + "Pobierz" "Edytuj" "Edytuj opis" "Edytuj ankietę" "Włącz" "Zakończ ankietę" "Wprowadź PIN" + "Przeglądaj przestrzenie publiczne" "Zakończ" "Nie pamiętasz hasła?" "Przekaż dalej" "Wróć" + "Przejdź do ról i uprawnień" "Przejdź do ustawień" "Ignoruj" "Zaproś" @@ -114,7 +129,9 @@ "Opuść przestrzeń" "Załaduj więcej" "Zarządzaj kontem" + "Zarządzaj kontem i urządzeniami" "Zarządzaj urządzeniami" + "Zarządzaj pokojami" "Wiadomość" "Minimalizuj" "Dalej" @@ -139,7 +156,7 @@ "Zgłoś treść" "Zgłoś rozmowę" "Zgłoś pokój" - "Resetuj" + "Zresetuj" "Zresetuj tożsamość" "Spróbuj ponownie" "Ponów próbę odszyfrowania" @@ -152,18 +169,21 @@ "Wyślij wiadomość głosową" "Wyślij" "Wyślij link" + "Udostępnij lokalizację na żywo" "Pokaż" "Zaloguj się ponownie" - "Wyloguj" - "Wyloguj mimo to" + "Usuń to urządzenie" + "Usuń urządzenie mimo to" "Pomiń" "Rozpocznij" "Rozpocznij chat" "Zacznij od nowa" "Rozpocznij weryfikację" "Stuknij, aby załadować mapę" + "Zatrzymaj" "Zrób zdjęcie" "Stuknij, by wyświetlić opcje" + "Przetłumacz" "Spróbuj ponownie" "Odepnij" "Wyświetl" @@ -181,6 +201,7 @@ "Ustawienia zaawansowane" "obraz" "Dane analityczne" + "Synchronizuję powiadomienia…" "Opuściłeś pokój" "Zostałeś wylogowany z sesji" "Wygląd" @@ -188,11 +209,14 @@ "Beta" "Zablokowani użytkownicy" "Bąbelki" + "Połączenie odrzucone" "Rozpoczęto rozmowę" + "Odrzuciłeś połączenie" "Backup czatu" "Skopiowano do schowka" "Prawa autorskie" "Tworzenie pokoju…" + "Tworzenie przestrzeni…" "Anulowano żądanie" "Opuszczono pokój" "Opuścił przestrzeń" @@ -213,6 +237,7 @@ "Pusty plik" "Szyfrowanie" "Szyfrowanie włączone" + "Kończy się o %1$s" "Wprowadź kod PIN" "Błąd" "Wystąpił błąd, możesz nie otrzymać powiadomień nowych wiadomości. Spróbuj naprawić powiadomienia w ustawieniach. @@ -238,6 +263,9 @@ Powód: %1$s." "Jasny" "Wiersz skopiowany do schowka" "Link został skopiowany do schowka" + "Powiąż nowe urządzenie" + "Lokalizacja na żywo" + "Zakończono udostępnianie lokalizacji na żywo" "Ładowanie…" "Ładuję więcej…" @@ -252,10 +280,12 @@ Powód: %1$s." "Wiadomość" "Akcje wiadomości" + "Nie udało się wysłać wiadomości" "Układ wiadomości" "Wiadomość usunięta" "Nowoczesny" "Wycisz" + "Nazwa" "%1$s (%2$s)" "Brak wyników" "Brak nazwy pokoju" @@ -264,6 +294,7 @@ Powód: %1$s." "Offline" "Licencje open-source" "lub" + "Inne opcje" "Hasło" "Osoby" "Link bezpośredni" @@ -282,8 +313,10 @@ Powód: %1$s." "Przygotowuję…" "Polityka prywatności" + "Prywatny" "Pokój prywatny" "Prywatna przestrzeń" + "Publiczny" "Pokój publiczny" "Przestrzeń publiczna" "Reakcja" @@ -291,19 +324,20 @@ Powód: %1$s." "Powód" "Klucz przywracania" "Odświeżanie…" + "Usuwanie…" "%1$d odpowiedź" "%1$d odpowiedzi" "%1$d odpowiedzi" "Odpowiadanie do %1$s" - "Zgłoś błąd" "Zgłoś problem" "Zgłoszenie wysłane" "Bogaty edytor tekstu" + "Rola" "Pokój" "Nazwa pokoju" - "np. nazwa projektu" + "np. nazwa twojego projektu" "%1$d Pokój" "%1$d Pokoje" @@ -317,6 +351,11 @@ Powód: %1$s." "Bezpieczeństwo" "Wyświetlone przez" "Wybierz konto" + + "%1$d zaznaczony" + "%1$d zaznaczone" + "%1$d zaznaczonych" + "Wyślij do" "Wysyłanie…" "Błąd wysyłania" @@ -327,12 +366,16 @@ Powód: %1$s." "Adres URL serwera" "Ustawienia" "Udostępnij przestrzeń" + "Nowi członkowie widzą historię" + "Udostępniona lokalizacja na żywo" "Udostępniona lokalizacja" "Udostępniona przestrzeń" - "Wylogowywanie" + "Usuwanie urządzenia" "Coś poszło nie tak" "Napotkaliśmy problem. Spróbuj ponownie." "Przestrzeń" + "Członkowie przestrzeni" + "O czym jest ta przestrzeń?" "%1$d Przestrzeń" "%1$d Przestrzenie" @@ -341,12 +384,14 @@ Powód: %1$s." "Rozpoczynanie czatu…" "Naklejka" "Sukces" + "Polecane" "Sugestie" "Synchronizuję" "System" "Tekst" "Informacje stron trzecich" "Wątek" + "Wątki" "Temat" "O czym jest ten pokój?" "Nie można odszyfrować" @@ -377,13 +422,19 @@ Powód: %1$s." "Wiadomość głosowa" "Oczekiwanie…" "Oczekiwanie na tę wiadomość" + "Czekam na lokalizację na żywo…" + "Każdy może zobaczyć historię" "Ty" - "Tożsamość %1$s została zresetowana. %2$s" + "%1$s (%2$s) udostępnił tę wiadomość, kiedy nie było Cię w pokoju, gdy została wysłana." + "%1$s udostępnił tę wiadomość, kiedy nie było Cię w pokoju, gdy została wysłana." + "Ten pokój został skonfigurowany tak, aby nowi członkowie mogli czytać historię czatu. %1$s" + "Tożsamość cyfrowa %1$s została zresetowana. %2$s" "Tożsamość %1$s %2$s została zresetowana. %3$s" "(%1$s)" - "Tożsamość %1$s została zresetowana" - "Tożsamość %1$s %2$s została zresetowana. %3$s" + "Tożsamość %1$s została zresetowana." + "Tożsamość %1$s %2$s została zresetowana. %3$s" "Wycofaj weryfikację" + "Zezwól na dostęp" "Link %1$s prowadzi Cię do innej witryny %2$s Czy na pewno chcesz kontynuować?" @@ -413,6 +464,7 @@ Czy na pewno chcesz kontynuować?" "%1$s nie mógł uzyskać dostępu do Twojej lokalizacji. Spróbuj ponownie później." "Nie udało się przesłać Twojej wiadomości głosowej." "Pokój już nie istnieje lub zaproszenie nie jest już ważne." + "Włącz GPS, aby uzyskać dostęp do funkcji opartych na lokalizacji." "Nie znaleziono wiadomości" "%1$s nie uzyskało uprawnienia do dostępu do twojej lokalizacji. Możesz włączyć dostęp w Ustawieniach." "%1$s nie ma uprawnień dostępu do Twojej lokalizacji. Włącz dostęp poniżej." @@ -424,6 +476,9 @@ Czy na pewno chcesz kontynuować?" "Przepraszamy, wystąpił błąd" "🔐️ Dołącz do mnie na %1$s" "Hej, porozmawiajmy na %1$s: %2$s" + "Udostępnianie lokalizacji na żywo" + "Udostępnianie lokalizacji w toku" + "Lokalizacja na żywo %1$s" "%1$s Android" "Wstrząśnij gniewnie, aby zgłosić błąd" "Zrzut ekranu" @@ -431,6 +486,14 @@ Czy na pewno chcesz kontynuować?" "Opcje" "Usuń %1$s" "Ustawienia" + "Nikt nie udostępnia swojej lokalizacji" + "Udostępnianie lokalizacji na żywo" + + "%1$d osoba" + "%1$d osoby" + "%1$d osób" + + "Na mapie" "Nie udało się wybrać multimediów. Spróbuj ponownie." "Naciśnij wiadomość i wybierz “%1$s”, aby dołączyć tutaj." "Przypinaj ważne wiadomości, aby można było je łatwo znaleźć" @@ -456,6 +519,7 @@ Czy na pewno chcesz kontynuować?" "Wiadomość w %1$s" "Rozwiń" "Zmniejsz" + "Udostępnianie lokalizacji na żywo" "Już oglądasz ten pokój!" "%1$s z %2$s" "%1$s przypiętych wiadomości" @@ -467,11 +531,15 @@ Czy na pewno chcesz kontynuować?" "Otwórz w Apple Maps" "Otwórz w Google Maps" "Otwórz w OpenStreetMap" - "Udostępnij tę lokalizację" + "Udostępnij wybraną lokalizację" + "Opcje udostępniania" "Przestrzenie, które stworzyłeś lub do których dołączyłeś." "%1$s • %2$s" + "Utwórz przestrzeń, aby organizować pokoje" "Przestrzeń %1$s" "Przestrzenie" + "Udostępnianie %1$s" + "Na mapie" "Wiadomość nie została wysłana, ponieważ tożsamość %1$s została zresetowana." "Wiadomość nie została wysłana, ponieważ %1$s nie zweryfikował wszystkich urządzeń." "Wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń." @@ -482,5 +550,5 @@ Czy na pewno chcesz kontynuować?" "Musisz zweryfikować to urządzenie, aby uzyskać dostęp do historii wiadomości" "Nie masz uprawnień do tej wiadomości" "Nie można odszyfrować wiadomości" - "Wiadomość została zablokowana, ponieważ urządzenie nie zostało zweryfikowane lub nadawca musi zweryfikować Twoją tożsamość." + "Ta wiadomość została zablokowana, ponieważ urządzenie nie zostało zweryfikowane lub nadawca musi zweryfikować Twoją tożsamość." diff --git a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml index 49351c6b64..68035e0cc2 100644 --- a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml @@ -300,7 +300,6 @@ Motivo:​ %1$s." "%1$d respostas" "Respondendo a %1$s" - "Denunciar um bug" "Relatar um problema" "Relatório enviado" "Editor de rich text" 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 dd15875590..d82855223d 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -291,7 +291,6 @@ Razão: %1$s." "%1$d respostas" "Em resposta a %1$s" - "Comunicar falha" "Comunicar um problema" "Denúncia submetida" "Editor de texto rico" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index f47d43b47e..33a39278a4 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -1,6 +1,7 @@ "Adăugați o reacție: %1$s" + "Adresă" "Imagine de profil" "Micșorați câmpul mesajului" "Ștergere" @@ -300,7 +301,6 @@ Motiv:%1$s." "%1$d răspunsuri" "Răspuns pentru %1$s" - "Raportați o eroare" "Raportați o problemă" "Raport trimis" "Editor text avansat" 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 bb423b5b10..a691d1d69b 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -323,7 +323,6 @@ "%1$d ответов" "Отвечает %1$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 e154093ddb..208016e363 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -1,6 +1,7 @@ "Pridať reakciu: %1$s" + "Adresa" "Obrázok" "Minimalizovať textové pole správy" "Vymazať" @@ -27,6 +28,7 @@ "Pozastaviť" "Hlasová správa, dĺžka:%1$s, aktuálna pozícia: %2$s" "Pole PIN" + "Pripnuté miesto" "Prehrať" "Anketa" "Ukončená anketa" @@ -47,6 +49,8 @@ "Vyžaduje sa časovo obmedzená akcia, na overenie máte jednu minútu" "Zobraziť heslo" "Začať hovor" + "Začať videohovor" + "Začať hlasový hovor" "Opustená miestnosť" "Profilový obrázok" "Používateľské menu" @@ -157,8 +161,8 @@ "Zdieľať odkaz" "Zobraziť" "Prihláste sa znova" - "Odhlásiť sa" - "Napriek tomu sa odhlásiť" + "Odstrániť toto zariadenie" + "Napriek tomu odstrániť toto zariadenie" "Preskočiť" "Spustiť" "Začať konverzáciu" @@ -306,7 +310,6 @@ Dôvod: %1$s." "%1$d odpovedí" "Odpoveď na %1$s" - "Nahlásiť chybu" "Nahlásiť problém" "Nahlásenie bolo odoslané" "Rozšírený textový editor" @@ -487,7 +490,7 @@ Naozaj chcete pokračovať?" "Otvoriť v Apple Maps" "Otvoriť v Mapách Google" "Otvoriť v OpenStreetMap" - "Zdieľajte túto polohu" + "Zdieľajte vybranú polohu" "Priestory, ktoré ste vytvorili alebo ku ktorým ste sa pripojili." "%1$s • %2$s" "Vytvorte priestory na usporiadanie miestností" diff --git a/libraries/ui-strings/src/main/res/values-sv/translations.xml b/libraries/ui-strings/src/main/res/values-sv/translations.xml index dfcd514209..afc58de8a5 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -281,7 +281,6 @@ Anledning:%1$s." "%1$d svar" "Svarar till %1$s" - "Rapportera en bugg" "Rapportera ett problem" "Rapport inskickad" "Riktextredigerare" diff --git a/libraries/ui-strings/src/main/res/values-tr/translations.xml b/libraries/ui-strings/src/main/res/values-tr/translations.xml index d92daa6527..a6d0b3e198 100644 --- a/libraries/ui-strings/src/main/res/values-tr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-tr/translations.xml @@ -264,7 +264,6 @@ Neden: %1$s." "Kurtarma anahtarı" "Yenileniyor…" "Cevaplamak için %1$s" - "Hata bildir" "Sorun bildir" "Rapor gönderildi" "Zengin metin editörü" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index 835ded33f9..db0fa31804 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -1,6 +1,7 @@ "Додати реакцію: %1$s" + "Адреса" "Аватар" "Згорнути поле тексту повідомлення" "Видалити" @@ -14,6 +15,7 @@ "Подробиці шифрування" "Розгорнути текстове поле повідомлення" "Cховати пароль" + "Інформація" "Приєднатися до виклику" "Перейти вниз" "Перемістити карту до мого місця перебування" @@ -27,9 +29,12 @@ "Пауза" "Голосове повідомлення, тривалість: %1$s, поточна позиція: %2$s" "Поле PIN-коду" + "Закріплена локація" "Відтворити" + "Швидкість програвання" "Опитування" "Опитування завершено" + "QR-код" "Реагувати з%1$s" "Відреагувати іншими смайликами" "Прочитано %1$s та %2$s" @@ -44,9 +49,13 @@ "Прибрати реакцію %1$s" "Аватар кімнати" "Надіслати файли" + "Локація відправника" "Необхідно виконати дію, обмежену в часі, у вас є одна хвилина для верифікації" + "Налаштування, потрібні дії" "Показати пароль" "Розпочати виклик" + "Розпочати відеодзвінок" + "Розпочати голосовий дзвінок" "Кімната більше не використовується" "Аватар користувача" "Меню користувача" @@ -64,6 +73,7 @@ "Зателефонувати" "Скасувати" "Скасувати наразі" + "Вибрати файл" "Вибрати фото" "Очистити" "Закрити" @@ -83,18 +93,22 @@ "Деактивувати обліковий запис" "Відхилити" "Відхилити та заблокувати" + "Видалити" + "Видалити обліковий запис" "Видалити опитування" "Скасувати вибір усіх" "Вимкнути" "Відкинути" "Відхилити" "Готово" + "Завантажити" "Редагувати" "Редагувати підпис" "Редагувати опитування" "Увімкнути" "Завершити опитування" "Введіть PIN-код" + "Дізнатися про публічні простори" "Завершити" "Забули пароль?" "Переслати" @@ -115,6 +129,7 @@ "Вийти з простору" "Завантажити ще" "Керування обліковим записом" + "Керування обліковим записом і пристроями" "Керування пристроями" "Керувати кімнатами" "Написати" @@ -154,6 +169,7 @@ "Надіслати голосове повідомлення" "Поділитися" "Поділитися посиланням" + "Ділитися місцезнаходженням у реальному часі" "Показати" "Увійдіть знову" "Вийти" @@ -164,6 +180,7 @@ "Почати спочатку" "Почати верифікацію" "Натисніть, щоб завантажити мапу" + "Зупинити" "Зробити фото" "Торкніться, щоб переглянути параметри" "Перекласти" @@ -184,6 +201,7 @@ "Додаткові налаштування" "зображення" "Аналітика" + "Синхронізація сповіщень…" "Ви вийшли з кімнати" "Ви вийшли з сеансу" "Тема" @@ -191,7 +209,9 @@ "Бета-версія" "Заблоковані користувачі" "Бульбашки" + "Дзвінок відхилено" "Виклик розпочато" + "Ви відхилили дзвінок" "Резервне копіювання бесіди" "Скопійовано до буферу обміну" "Авторське право" @@ -199,6 +219,7 @@ "Створення простору…" "Запит скасовано" "Виходить з кімнати" + "Вийшов із простору" "Запрошення відхилено" "Темна" "Помилка розшифрування" @@ -216,6 +237,7 @@ "Порожній файл" "Шифрування" "Шифрування ввімкнено" + "Завершується о %1$s" "Введіть свій PIN-код" "Помилка" "Сталася помилка, ви можете не отримувати сповіщення про нові повідомлення. Усуньте неполадки зі сповіщеннями в налаштуваннях. @@ -242,6 +264,8 @@ "Рядок скопійовано до буфера обміну" "Посилання скопійовано в буфер обміну" "Під\'єднати новий пристрій" + "Місцезнаходження в реальному часі" + "Показ місцеперебування наживо завершено" "Завантаження" "Завантаження наступних…" @@ -270,6 +294,7 @@ "Не в мережі" "Ліцензії відкритого коду" "або" + "Інші варіанти" "Пароль" "Люди" "Постійне посилання" @@ -288,8 +313,10 @@ "Приготування…" "Політика конфіденційності" + "Приватний" "Приватна кімната (тільки за запрошенням)" "Приватний простір" + "Публічний" "Загальнодоступна кімната" "Загальнодоступний простір" "Реакція" @@ -304,7 +331,6 @@ "%1$d відповідей" "Відповідь %1$s" - "Повідомити про ваду" "Повідомити про проблему" "Звіт подано" "Багатоформатний текстовий редактор" @@ -325,6 +351,11 @@ "Безпека" "Переглянули" "Вибрати обліковий запис" + + "%1$d вибраний" + "%1$d вибрані" + "%1$d вибрано" + "Надіслати до" "Надсилання…" "Не вдалося надіслати" @@ -334,12 +365,16 @@ "Сервер недоступний" "URL-адреса сервера" "Налаштування" + "Поділитися простором" "Нові учасники бачать історію" + "Поділитися місцезнаходженням в реальному часі" "Поширене розташування" + "Простір спільного користування" "Вихід" "Щось пішло не так" "Ми зіткнулися з проблемою. Будь ласка, повторіть спробу." "Простір" + "Учасники простору" "Про що цей простір?" "%1$d простір" @@ -349,12 +384,14 @@ "Початок бесіди…" "Наліпка" "Успіх" + "Запропоновано" "Пропозиції" "Синхронізація" "Системна" "Текст" "Повідомлення третіх сторін" "Гілка" + "Гілки" "Тема" "Про що ця кімната?" "Неможливо розшифрувати" @@ -385,14 +422,19 @@ "Голосове повідомлення" "Очікування…" "Чекаємо на це повідомлення" + "Очікування геолокації…" "Будь-хто може переглянути історію" "Ви" + "%1$s (%2$s) поділився цим повідомленням, оскільки вас не було в кімнаті, коли його надіслали." + "%1$s поділився цим повідомленням, оскільки вас не було в кімнаті, коли його надіслали." + "Згідно із налаштувань цієї кімнати, нові учасники можуть переглядати історію. %1$s" "Ідентичність %1$s скинуто. %2$s" "Ідентичність %1$s %2$s скинуто. %3$s" "(%1$s)" "Ідентичність %1$s скинуто." "Ідентичність %1$s %2$s скинуто. %3$s" "Відкликати верифікацію" + "Надати доступ" "Посилання %1$s спрямовує вас на інший сайт %2$s Ви впевнені, що хочете продовжити?" @@ -422,6 +464,7 @@ "%1$s не вдалося отримати доступ до вашого розташування. Повторіть спробу пізніше." "Не вдалося завантажити голосове повідомлення." "Кімната більше не існує або запрошення не чинне." + "Будь ласка, увімкніть GPS, щоб отримати доступ до функцій, що використовують геолокацію." "Повідомлення не знайдено" "%1$s не має дозволу на доступ до вашого розташування. Увімкнути доступ можна в Налаштуваннях." "%1$s не має дозволу на доступ до вашого розташування. Увімкніть доступ нижче." @@ -440,6 +483,14 @@ "Варіанти" "Вилучити %1$s" "Налаштування" + "Ніхто не ділиться своєю геопозицією" + "Обмін геопозицією" + + "%1$d особа" + "%1$d осіб" + "%1$d осіб" + + "На карті" "Не вдалося вибрати медіафайл, спробуйте ще раз." "Натисніть на повідомлення і виберіть \"%1$s\", щоб додати його сюди." "Закріпіть важливі повідомлення, щоб їх можна було легко знайти" @@ -465,6 +516,7 @@ "Повідомлення в %1$s" "Розгорнути" "Згорнути" + "Спільний доступ до місцезнаходження у реальному часі" "Уже переглядаєте цю кімнату!" "%1$s із %2$s" "%1$s закріплених повідомлень" @@ -477,10 +529,14 @@ "Відкрити в Картах Google" "Відкрити в OpenStreetMap" "Поділитися цим місцем перебування" + "Налаштування обміну геопозицією" "Простори, які ви створили або до яких приєдналися." "%1$s • %2$s" + "Створіть простори для організації кімнат" "Простір %1$s" "Простори" + "Надано доступ %1$s" + "На карті" "Повідомлення не надіслано, оскільки підтверджену особистість %1$s скинуто." "Повідомлення не надіслано, оскільки %1$s перевірив не всі пристрої." "Повідомлення не надіслано, оскільки ви не підтвердили один або кілька своїх пристроїв." diff --git a/libraries/ui-strings/src/main/res/values-ur/translations.xml b/libraries/ui-strings/src/main/res/values-ur/translations.xml index 8e9c0fb3be..7bf138deb5 100644 --- a/libraries/ui-strings/src/main/res/values-ur/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ur/translations.xml @@ -212,7 +212,6 @@ "بازیابی کی کلید" "تاکہ کر رہا ہے…" "%1$s کا جواب دے رہے ہیں" - "ایک خطاء کی اطلاع دیں" "کسی مسئلے کی اطلاع دیں" "گزارش جمع ہوگئی" "امیر مدیرِ متن" diff --git a/libraries/ui-strings/src/main/res/values-uz/translations.xml b/libraries/ui-strings/src/main/res/values-uz/translations.xml index 2542de084e..304a044e0b 100644 --- a/libraries/ui-strings/src/main/res/values-uz/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uz/translations.xml @@ -317,7 +317,6 @@ Sababi:%1$s." "%1$d ta javob" "%1$sga Javob berilmoqda" - "Xato haqida xabar bering" "Muammo haqida xabar bering" "Hisobot topshirildi" "Boy matn muharriri" @@ -410,6 +409,7 @@ Sababi:%1$s." "Tarixni hamma ko‘rishi mumkin" "Siz" "%1$s (%2$s) bu xabarni ulashdi, chunki u yuborilganda siz xonada emas edingiz." + "%1$s bu xabar yuborilgan paytda siz xonada bo‘lmaganingiz uchun uni ulashdi." "Siz yuborgan xabarlar bu xonaga taklif qilingan yangi a’zolarga ulashiladi. %1$s" "%1$sning raqamli identifikatori qayta tiklandi.%2$s" "%1$sning%2$s raqamli identifikatsiya qayta tiklandi.%3$s" diff --git a/libraries/ui-strings/src/main/res/values-vi/translations.xml b/libraries/ui-strings/src/main/res/values-vi/translations.xml index 9a450117ac..2db0ce1dae 100644 --- a/libraries/ui-strings/src/main/res/values-vi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-vi/translations.xml @@ -310,7 +310,6 @@ Lý do: %1$s ." "%1$d trả lời" "Đang trả lời cho %1$s" - "Báo cáo lỗi" "Báo cáo sự cố" "Đã gửi báo cáo" "Trình soạn thảo văn bản nâng cao" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 11eb111b49..819c993bb1 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -311,7 +311,6 @@ "%1$d 個回覆" "正在回覆%1$s" - "回報程式錯誤" "回報問題" "已遞交報告" "格式化文字編輯器" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 974e91b530..00bb686f78 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -13,6 +13,7 @@ "加密详情" "展开消息文本框" "隐藏密码" + "信息" "加入通话" "跳转到底部" "将地图移动到我的位置" @@ -67,6 +68,7 @@ "通话" "取消" "暂时取消" + "选择文件" "选择照片" "清除" "关闭" @@ -125,7 +127,7 @@ "管理账户与设备" "管理设备" "管理房间" - "发送消息给" + "发送消息" "最小化" "下一步" "否" @@ -316,7 +318,6 @@ "%1$d 个回复" "正在回复 %1$s" - "报告 bug" "报告问题" "报告已提交" "富文本编辑器" @@ -456,6 +457,8 @@ "抱歉,发生了错误" "🔐️ 在 %1$s 中与我一起" "嗨!请通过 %1$s 与我联系:%2$s" + "正在共享实时位置" + "位置共享正在进行" "%1$s Android" "摇一摇以报告 bug" "屏幕截图" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index cdb3c52e6a..656ec50569 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -325,7 +325,6 @@ Reason: %1$s." "%1$d replies" "Replying to %1$s" - "Report a bug" "Report a problem" "Report submitted" "Rich text editor" @@ -468,6 +467,9 @@ Are you sure you want to continue?" "Sorry, an error occurred" "🔐️ Join me on %1$s" "Hey, talk to me on %1$s: %2$s" + "Live Location Sharing" + "Location sharing in progress" + "%1$s Live Location" "%1$s Android" "Rageshake to report bug" "Screenshot" diff --git a/screenshots/de/appnav.root_RootView_Day_0_de.png b/screenshots/de/appnav.root_RootView_Day_0_de.png index 7f70b90751..2d0d9fab03 100644 --- a/screenshots/de/appnav.root_RootView_Day_0_de.png +++ b/screenshots/de/appnav.root_RootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa11b2165af4a12cd518fb3f9a06b91d9ce87b468705efc7a962bf34a8278fac -size 26284 +oid sha256:c3082552bf96131ddba157697bb254e22d6277537759c32dfabb56a2e99a9138 +size 27116 diff --git a/screenshots/de/appnav.root_RootView_Day_1_de.png b/screenshots/de/appnav.root_RootView_Day_1_de.png index e1796d211b..6d1c994b27 100644 --- a/screenshots/de/appnav.root_RootView_Day_1_de.png +++ b/screenshots/de/appnav.root_RootView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35a9ac561c2546fd94121cb0c80e1f5be1fe147f425be4d087b647ae0f204afd -size 29986 +oid sha256:f9231272736124f1e54a3422310d2dd5c7ad81e850548886062df61de36165c4 +size 30868 diff --git a/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_de.png b/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_de.png index 79a282bf32..5fb0408a49 100644 --- a/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_de.png +++ b/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:141bf23853b7ba97cffd4d54e9e0a48843d0a49e7f6d6a91c41f110d2df1c7dd -size 24180 +oid sha256:d20416987d2485431b66ebb29af1f03efa2e04da7ef5875eda62d7c5e187052b +size 24828 diff --git a/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_de.png b/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_de.png index bc89b471f2..4aa0388c8a 100644 --- a/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_de.png +++ b/screenshots/de/features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a39d0a1c752533bfb097ad1d2c59978123545ee76259e26efe6e3fbae209db69 -size 31441 +oid sha256:62d531d6b01266019e2fa686321e93d64bcffd5b1786f7ad4efac83fcc2a2fc5 +size 32141 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_de.png new file mode 100644 index 0000000000..048facf45b --- /dev/null +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b4ead892d27f2eb5fd7c9319e3c954ab2ae57c68180e08b30c07741c53cadda +size 35383 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_de.png new file mode 100644 index 0000000000..af1fe3af55 --- /dev/null +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b481b2bafe288542d25c301aa568fdbd9d63170b1c59b01fdacacc69d0395b3 +size 17352 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_8_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_8_de.png new file mode 100644 index 0000000000..28afdb624e --- /dev/null +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6f6d2946d194ba97c64e6749b1f9d8bf4ce52e685d06631cf22b47e156791d1 +size 41526 diff --git a/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_9_de.png b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_9_de.png new file mode 100644 index 0000000000..9c540f1299 --- /dev/null +++ b/screenshots/de/features.lockscreen.impl.unlock_PinUnlockView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b74c5fd9cb084ca2c391ea65dea23ecc027d1206510841ada0d28d5f6674995 +size 32940 diff --git a/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_de.png index 67fec85a88..b3f6706cda 100644 --- a/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68d468cf24f004f75b5f0d0ab21e4576941749b32a6780b074fce5ba6e7e06c0 -size 56172 +oid sha256:29fb6c5ccbc3fc1196fbccc4ce18d10668ae47733ecfcc454354ff231cac0f10 +size 56030 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_de.png index 72c99d1da9..3ece744fcb 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b10f43a24d80689ee7c929eb5c530b544891cb0dce63ab9bbb4618f999fbd3cd -size 6226 +oid sha256:cbc7e8874d0a3c25aa0e6036c1ffc365c5407d79b011e5321821691315e98d3a +size 6223 diff --git a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_17_de.png b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_17_de.png index 75eb3ccc78..abb112a538 100644 --- a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_17_de.png +++ b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68cef5541309a99a95f5004cbdcb4d091a70f68c142de2ff78deb710c521ea30 -size 69820 +oid sha256:4c119e44d9f51d0b4786f06092d3d0138ef69b363c43303164b0aac0629b324e +size 69840 diff --git a/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png b/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png index 99f0c161e8..2190897314 100644 --- a/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png +++ b/screenshots/de/features.messages.impl.topbars_MessagesViewTopBar_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d23534a940951085437a3c739b58d515a968869227db794a149b5066abe21800 -size 58473 +oid sha256:f952ad332aea788e87445b304894f0acf77a6ca5498d67584ab4e727cbb16eb4 +size 58055 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 99f738df12..9827329bbd 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:fe130f936313191f602331d5b1a08dd025da47a66ce23dd71cee0665e951a3ac -size 44506 +oid sha256:ece0f3e49ca489a67aa7a489bc2d7e4f35a99cff4e4f2df3f7ab480965b3f972 +size 44150 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 47113e66d0..28e4e403fe 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:1228309dd9c2dbaa340f1b941ca9da17067c619a8645e870f20bf39db7c9ba25 -size 27560 +oid sha256:2b8741879622a799c9cae5ba7ecb0f83700cdfca52dd9e6b281bfa0cc31267b3 +size 26817 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png index 12791633f9..2f7d54a7ac 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b095c101503364437c57050c56fbcc56e8187a697921364eee4ae0807ad684e -size 38449 +oid sha256:61958e2bf98bcc380bca8622d53ea8d46ec466de6e8da134a9b01ea1605eca6c +size 38040 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png index edfa967da9..61aba383d1 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:128e6931b4ac3fbc02138fba8a67e22f60e722acddd8db26b1b031102bcbb198 -size 28060 +oid sha256:3e8275f65773690627383ef7f030f270c7f93639af5dff40ca46ee0c2d5e0316 +size 27628 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png index efb26f07c5..672f329d0d 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc9e3439b97efbd490030a7fdae43bccd60193b18c658f4ced99959550fc77bd -size 29481 +oid sha256:3efe6d5c4b5f02679eddf9be90af0ee0648a576a4488c021d9d7601aac3c71ab +size 29060 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png index 01f0712a7a..5d238d5c65 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca99f565a56588d126e357df4417469e39ff196d21d41cdbff05166b8356da1 -size 21419 +oid sha256:17f591ce8aa0dc3c79dd450dc58b84194625f1eee1b8fb3d5dbd67b3b1e07966 +size 21035 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 2b260ff9f6..ba560e522f 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:a3b21d14f077ebcaa11ac2dca31fb08f9dc21f26a4847c6d1c623f0ec953cdc2 -size 45806 +oid sha256:7fff1e6cc2fe7ae2fe3dc81ee1f13f096245c238f3ca946ba4a353e693d0ecb8 +size 45468 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 a584834ca9..893d7a086f 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:bff3f28233c7b1be7c3524f06258dfcf27dffa4251279beb2bacb883c3c86c41 -size 28576 +oid sha256:e335a7132330e2c5dfb3f4cc015e25fdf67af2030474b347df2e0cca343b1c74 +size 27763 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png index bd510c21a9..7a2abd7f42 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f3ff8d40a465f3d73143de0d66566c0129a794cdba9556bb54423b2110375c8 -size 39033 +oid sha256:645d02f8d4503edfc2e5fb756050ed1ddb8a692d715d06b84b232e744379d613 +size 38820 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png index bc7f862d77..3b526c2fc1 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a7aab1f7cc5a26672666fab13a36fd7bb47d524b9ffa172bac322c1ad1c3b40 -size 28583 +oid sha256:4df58e4d7f0baeb0bfd0225302ed8e69eba1edc0ef37ae6cb8b86ffdd057b924 +size 28414 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png index 7cd952fef4..57bed01c00 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4743cbd8f565d114a74024455bddd999df95fba4c0ca346b32203cf6fb6f7550 -size 30203 +oid sha256:1a24be625e8a7d6eeb46f0066247bc22bd367b0e035802b54a663478392f4289 +size 29980 diff --git a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png index 9cbde40654..4537f0ff7f 100644 --- a/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png +++ b/screenshots/de/features.preferences.impl.root_PreferencesRootViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a7e61a79e28968dfe2f57b75b88125471fecccdf4af670c7f2ca238019fc31c -size 21527 +oid sha256:f7a69722f7e5f6373a266116e7d97bef5e620b136deb2cea30409c41ec8d4180 +size 21310 diff --git a/screenshots/de/features.rageshake.api.crash_CrashDetectionView_Day_0_de.png b/screenshots/de/features.rageshake.api.crash_CrashDetectionView_Day_0_de.png index 29240c9037..6fe8d49af7 100644 --- a/screenshots/de/features.rageshake.api.crash_CrashDetectionView_Day_0_de.png +++ b/screenshots/de/features.rageshake.api.crash_CrashDetectionView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d34090b6d2a05946f81c205b40dba420f49c19063b8f4bf2d0f55afe31886e7 -size 24441 +oid sha256:225e3ea78d7f1bf3bb9c2c56c128bdb6994b22df3f7c010837ef37bbcdc34997 +size 25170 diff --git a/screenshots/de/features.rageshake.api.detection_RageshakeDialogContent_Day_0_de.png b/screenshots/de/features.rageshake.api.detection_RageshakeDialogContent_Day_0_de.png index 39aa8e9965..c78691eca9 100644 --- a/screenshots/de/features.rageshake.api.detection_RageshakeDialogContent_Day_0_de.png +++ b/screenshots/de/features.rageshake.api.detection_RageshakeDialogContent_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2e6807ba4b85c71cca8aebaf34a22c836c6d53ebf8caaa42790594d3066956c -size 27850 +oid sha256:7546c33148ccc5d46e112c2488c6aecce925a9a0d729cc9c029c9dac2578e2a7 +size 28753 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index 759f942a33..c39909cf49 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:088e737cec6fb5fbd624a16dbbcca45a8815441b93ef4d949acbaea9d9c52c3c -size 49472 +oid sha256:10355aa4b00cbaa5ccded1d03d23db71907f9943ebd40dfec50545c046991b61 +size 49461 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index 10ad61fad0..7e18f43981 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2f53b1fbc09307314ad92c688ef26490520de7380d27cab1ca67aca6e34a655 -size 47718 +oid sha256:7a2132a405f40d577a61420b4ad2d7bb9f55f67b5fe1b07b4ddb0daff10b8b89 +size 47704 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index e95dd4de1d..4cd5290a7c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bab8c4a37f2d9fbd4b29cdd729db9a84216fcfae3023facbfb979ecdf4645b6 -size 46428 +oid sha256:e3003b423f3fa7504581ecf60cfbb82452eb17267bbc44f3b9439642da0ff096 +size 46416 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index 32193ce5f1..0086307ec6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6a11d9f2402b756b61a5ab3bb766bf19404fb24b306f86634650b070554268b -size 48264 +oid sha256:5bca6f953108331e512e3b0bca6086be207c1b1d2835d1dc220673098f5e3a9d +size 48263 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index a29bbd09cb..0a99e31a52 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b37442dd675bf8ce133b57484ec8dc11c695df69907ead762fa4b79c7c978663 -size 48172 +oid sha256:494b1135a849734bce082b30aaeb491aebdb95cd67e6150be7334b55fe9fc0eb +size 48159 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png index 0fd4f66fcc..110a14531d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa879e0d1483b07c3127ed07da87b8aabcc38c51db6e158ff29039e3b1886745 -size 49071 +oid sha256:2e147c903449a6c5ba6fa5041f73217c29965c2b4c2a165a96d36aebd2e2691e +size 49059 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png index 6100b21322..469a77a49a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05db3b3e42f59515ffad1c522496ebff6092bd458297ca92eba25b321d8be0ca -size 49564 +oid sha256:5590f66df4e26885e4b4776de840a9b1e4877a579f34769fe652172d01f5e374 +size 49554 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png index 63eb0980b8..7235a7a8e9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5662df69d4f1c66bb47019096df83031b190d7bc8e0b354c06f09d7c25d3773f -size 48454 +oid sha256:bbcc418cc648fe137d32a0455af4c0235c5c9269c49826c28dcdb22e7997638e +size 48441 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png index 44c943a262..6389db0432 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81ac09d41d5f08d8f4714f070c3661e0628a0d28f32d58eb0a1460eacb1be8d0 -size 47752 +oid sha256:a8d7ee825e1e2cb28babc26bdd2de3747c99bff0b95f6c7e4d8a2b2dd9f9dd49 +size 47758 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png index cf0d675e49..89c1174fd0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2dadbbe0ad65da59d7599f782e3893375867821ba0b8309c61a94f530cd58965 -size 44846 +oid sha256:2e6f13ca775952d7a06d82c41519a5f480f156eabd015a2020e0a5cf9d435765 +size 44694 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png index 3b5b87ac98..456f8db66c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d8648c9c6297f2e8ec87b334e1e885555e921faa7d98dda70fffb902ebe2688 -size 44828 +oid sha256:c84a92f1c008dffb95cf4aa1337a35ba208d3702decf912e084937665079d947 +size 44674 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index c418b7d0b4..2f9e2bc268 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc469282e83bb5e88a173fc8a4500f8260f37ffc8afb20449e1126aa50631693 -size 43177 +oid sha256:29f45a3272a173614152dd6e7b0d3dc08de6d3b1f86c9683b267e97db773fb19 +size 43163 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png index 8360619979..fb90141dde 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a0a8cafff67aa2fd2991ce4320ed412cfebed0208b2d1c4e8bc0f45e7977e6b -size 48534 +oid sha256:cc70b1f6c6152f0f57b4f8586d781938c12af8bc61b517a232b779739eea6c72 +size 48521 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png index d0a134673b..10f31671ce 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e1625ee06b9ae0025bf798705c8919b405f9cd917d85056623618fe0223e736 -size 48450 +oid sha256:8e5d32aea59c328b24d0a56bede3110e5b4a1d90a9b5eeb9bffffd30de5a41b6 +size 48437 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png index 720c2eeae6..2a3f4a388a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aaf88dcf5e06d6c409318447e154b9d0b33775ad3e3aa8d8e3fe2b85aedd816 -size 48017 +oid sha256:ddddf06d51977bcd997e3680d2fe3ff4f9d8102848980cf88b4d2ef08e37d5f9 +size 48004 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index e95f8da31b..28f7d63b1a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae9087dda9b2c6e160aa14dbb45e8ab614f2c315158c5bfc48d5c67058ebb63c -size 42639 +oid sha256:b5ffb5c4bb36e659d6650acfc43c560d6c6cfc26f7b663e6de036c8d210c3c53 +size 42627 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index ac8ea4aa9b..b945a5f082 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d1c6f0c3898e000774de48cd7590c44bc11c297163735f16cc2dccaaf23e538 -size 45428 +oid sha256:cd4f6d16dbb22cd3968e4fb3a1408ee6387bb3f7d81388b18e4b4d7cdc24a2b8 +size 45416 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index aeb6c18836..291e381783 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff3ddf54027d1f77d8e1e1f5cc063f1c13fc2725447d7cb6475c2ebb6c84b529 -size 47002 +oid sha256:19e656cf990a24bb08da6b7205a5fc1d39c76bbec967781cf0ea2a55bf7d11ab +size 46989 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index a600833dc2..8ad5518a4c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c99f882e777e43d1689ffa80a5396583426d987725424c779eff019f96d1c1bb -size 44444 +oid sha256:df3eb3ec43a8219be6e5fb27ae316222bf049d73d897712e9e4981c7cd482f66 +size 44290 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index a4d831a49c..2524ad82a5 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35aab3535b161bb3b111a9a352f510b43a25f9c98a7e5648aa3a380cd8402063 -size 45969 +oid sha256:babe700b98176da34b30db8fcd367f13b26eb0b8216f8a486d0462dccdbe3068 +size 44782 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index 9d4d6b35bf..d3fe3eeec7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d637a9ab06a5eb79bbee7d141be92563d1c6d4acbb301b61a8c02db53e89132 -size 49078 +oid sha256:e78510f867c14a8cc9e98d6bf36880136f36cdf08cf8940b9a5f54e327a1f115 +size 49064 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index 0884641063..ae3e083f08 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f45315776c2e841043aabdef8b72a8cd4e485270ada198b28f92de8f55bc7ca -size 48392 +oid sha256:627f61625aadbd1801d38dec33a107c72d9c2bc10057ab85ed15207557fb8343 +size 48379 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index 82093102db..e52d7bbc87 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:401e526ee8c3e27e90d34a9cb86561789172a9b91b5b5a6f5955aa4b001dfb1a -size 47658 +oid sha256:d3da232ee647be1a9722a00d4884f6f559cddc83f642aa07c3cbc4fb35746028 +size 47649 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index d7778998b9..a825b90d21 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f7fd6001307e447476310fc91cfe2eab0bddd8d0bc137a9a35698320f30f706 -size 50559 +oid sha256:3b17db5e3de5f2a7cd8ab5adb998b9343993aeb4ca7e072287e0f60064638155 +size 50517 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index 777eaaf717..1e506d9628 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17c582d1666c5c7b6040b007a13f0c6367eccd07d2a35847e4751bf8e5de7ed6 -size 48691 +oid sha256:e9165fa335486aa0463e9ee43b9e7c581a1c9bcd81f605a193d217fac58bff01 +size 48642 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index c59448b50d..15456d858a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:673f1524b9a0e8284600cdcc5536bdf98574424c31a8a35552a75cbdd404424d -size 47490 +oid sha256:66d934f6d2fb764fd8d856e19671a47eb96bf79d1904222f12ba2c04ff815472 +size 47449 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index f0c70fc293..759eda79f1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7aff67290ac05d786c65cdc2dabc4bc6dbf60a802d0b77ae8c8404fd05e1bb7 -size 49256 +oid sha256:f6dfedba8b70af6389b88143e0c1c59dc9dbacba06b985fb791f9d5ef6b76d65 +size 49222 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index 4c43697ac5..b50b33e08a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec0e7d344810f5fa43b9bc05c41116501350e3daedabd33c97782082264369ab -size 49157 +oid sha256:114059cd1d74d7e67039fdab4f4a37306badb38b2811b7cb494fd47a3de7dd52 +size 49118 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png index f0d89fc365..09a418c30b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a012a170cbd17e1fe588c27e41e531baa12097830ed205ac5c9c51c5bd62e003 -size 50108 +oid sha256:319b86e201bbcf3efb4ca2cceef15e7bba83800202c110439b5bab58d94aaa9d +size 50069 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png index 865602ac62..9bb6b27c92 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f734bad723be9fada8b38763321ea63778f0ebfd65567472432ac32634d7b67 -size 50695 +oid sha256:0ddeb573fd6c83a7b0d9e00947a1f1b87c27eda69796285c7b9a6cee487ccbb1 +size 50653 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png index 1a59d9ec17..8c769eb3c2 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a3c70bad40927fcd45902da33a3fe12fa50057e278c6009b1ed5befe4512182 -size 49470 +oid sha256:9a092056f5ae3f2bf40db0e1727afd191bf19b2bffea9bbeaa9cd5822b2235af +size 49426 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png index 1c01af3450..b7e8d5e7da 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9e7f2edac607fcd372a991078a4d9d92e01e148ddebce2785174dbf877e14bf -size 49045 +oid sha256:f300dbb1b0868fa619edef30e43231b5c2cb777bee2af56a8f3dfe42253db318 +size 48999 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png index aa64291503..602a4de377 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:755919c37c18cac3fe595975847620ce4a0b836b01546fbbfd3cdd1693ad5435 -size 45945 +oid sha256:2b7f04ee27a8cdc0b9cde949d6167f013cbf2cbfcf591a54262ca697fd54ab57 +size 46011 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png index 1cb9dd897b..0bb73f7526 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:735d92a8a39f4ec22ab51830cc0a5877697d028a12ba4cec2c2cce0475cddea5 -size 45888 +oid sha256:1d39a3067e1df646444276e13c1709e5c0ab0b27bb313531bdedde43fa056d6c +size 45947 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index 1d527ce0e6..286ac63579 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c9e86d87f93e3e96ee25b6090b0f989baf173a189a3c2274dc2cdd06dd566e1 -size 44223 +oid sha256:62ec3e5198b9d783cc7dcff94c39dec2ddd7edabbcbf1e272418e5446ec6d854 +size 44182 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png index c6e83dd003..673cdd0c30 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ed0a0e971a25574d13b6bbc5e0137f8a3f2bb1909d6149d16e09199dce3c25a -size 49535 +oid sha256:60c1951da07af010cd27d8622dcef6f953d369df9143a6c09e479bd26ea3aea9 +size 49487 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png index dd2ebef37e..49483a49d7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd5dcb36fc1e9cb675d461850e88f80402d30e43c6f8709ab3274326ad4a42b5 -size 49454 +oid sha256:951b4ed6186db857355229f9518aab6e72d0e6089e750eb58f1d21c23af54a58 +size 49415 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png index 9c0a4282b1..e49a2edf1b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b24f32d9a8640b7d1ef5a2b77d6198d3dbfabcab7a9cf46af8a0b24e23655eeb -size 48999 +oid sha256:9036a65f211de57afb08e2a8a1edc679690286063855146616d1a9205f5e92ba +size 48963 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index 24966de76f..037c1a51b9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e92a8647bc6be119cb6dd401267f3369ad0ffa61372efc21d8e1752350c37a54 -size 43617 +oid sha256:c42fc0297235872ed4a405cbf73dbe68646b4ba2f2d8b4ab2a2c759372aedff5 +size 43575 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index 95c8bffb80..c7dd2f7b1a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39e4eab91f3d07daff1437991ec74005e7c79eed3f9b4ff71c154d68bbce3e71 -size 46400 +oid sha256:36df2a8757b8fea88e4d03150b7c9f1349e4112b1c7172224818cf394eb8755f +size 46360 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index 5c5bc77cb2..731d5599be 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ce073c24e1d2b0cfcc6fc64cc62042c5bb9260e7636441827fdc746964aefbe -size 48017 +oid sha256:73a5e4314e7d2cfffff54c1181aab1fb95227172758299c06754e97b59a58bba +size 47972 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index 0a07cb8799..fb6a42fde4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19fa23046e80df108a9d9cabe2b34d713e12d74127b3c4a320b5770e286429ea -size 45506 +oid sha256:9dec4b40543155b80a9d1db51a97f269d6188c0ea84384abf37e7c32a953a4c3 +size 45565 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index 71787b6f1f..6ce7d985b9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb164e2c9796a1d12a0146b3fb67d29813be54d42b0628d17a323a525b866177 -size 47065 +oid sha256:2d575f4e716de7fc5e1d376a8225f69bcf5375ea8a1555baa8d27d0481b45d4b +size 46038 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index a9ebd612f5..28cb1f210b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d764589c800027ef6f34660324d980c6525475b0a65d7f1e7b4afa4f4f19aaa -size 50155 +oid sha256:8a4d42087d391ecc107d9f485e97499a9952f85c7555ac4428d36f4565c75dba +size 50112 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index 9a167dc9b8..8c515e1bfe 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3784f50878f750bc04b45ca657ccb89d74343164ef7b0aa06fb43260f46ef72 -size 49429 +oid sha256:a5b97717f51c11b13b760cdb6922587db19d962f1f47c7c45bb39de2fc7880af +size 49374 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index b2f44c24d5..04877ee746 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91302b37b756a39cae34160110d2f7bb3fd67129343cde63ae22b109861299c7 -size 48671 +oid sha256:29a2b72b3ab467665c819763eee041fe349d26d9d2b6b724677a071dfcb84450 +size 48626 diff --git a/screenshots/de/features.startchat.impl.components_SearchMultipleUsersResultItem_de.png b/screenshots/de/features.startchat.impl.components_SearchMultipleUsersResultItem_de.png index 34d3e5d494..e86f031a8e 100644 --- a/screenshots/de/features.startchat.impl.components_SearchMultipleUsersResultItem_de.png +++ b/screenshots/de/features.startchat.impl.components_SearchMultipleUsersResultItem_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:993191e7db24d10111a3b7c22c0e192e28e9a3e170edb57de14ec205de73cf15 -size 95383 +oid sha256:fa263cc9cfb7cc47dacfa4ea65a9f4e8660e1a17df61e357d5cf76928dd89100 +size 90714 diff --git a/screenshots/de/features.startchat.impl.components_SearchSingleUserResultItem_de.png b/screenshots/de/features.startchat.impl.components_SearchSingleUserResultItem_de.png index 8d1aac76c6..3eff580ee8 100644 --- a/screenshots/de/features.startchat.impl.components_SearchSingleUserResultItem_de.png +++ b/screenshots/de/features.startchat.impl.components_SearchSingleUserResultItem_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93b60fdeb5bd6fbf7f3ab3eca81f2109b02101bec9a8c812ceb02ac8d19840e2 -size 51866 +oid sha256:caa936c590efb400eec2e3d238bec4e48b81883371057f34badf0abb1644554c +size 49654 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_0_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_0_de.png index 502b87bbdb..1230b616fd 100644 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_0_de.png +++ b/screenshots/de/features.startchat.impl.root_StartChatView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7884a15f505913ba13dcd99320b1b9d26cbf1e8f15eb051e65d8fbedc1a828c -size 27673 +oid sha256:fa14bce431debf9d9a64c4a47bed9d89730fb21d480af186dc2ac53a9e52f1af +size 31785 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_1_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_1_de.png index 33479d694d..14b7d796bc 100644 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_1_de.png +++ b/screenshots/de/features.startchat.impl.root_StartChatView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cea006dcb900e70cc9f4be90b9144e340768e7d23d90932c99422eaf8ea96fb -size 21203 +oid sha256:0f4fe803d3318a31919099958d793ecb4f5eae7109596712de23c89e6c982ea4 +size 20145 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_2_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_2_de.png index e4c6ad93ff..358883997e 100644 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_2_de.png +++ b/screenshots/de/features.startchat.impl.root_StartChatView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41ddc90c4f22013037b7786f2b91e35dd2db7329d43f12cb72f8cd1474e89d3c -size 31725 +oid sha256:a99cb9da94e1ea3050ccb1e9c32380fde1b5e55ea806c4c2aa5f7bc8c5d0b0fd +size 30750 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_3_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_3_de.png index 3e3fcc2526..fc81f7c584 100644 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_3_de.png +++ b/screenshots/de/features.startchat.impl.root_StartChatView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a17fadc85a373556a338cd4c9ceaf35432b1853e06641237e6d3f4bf7cd0979 -size 51495 +oid sha256:af238ae63b675069702e65b52c1e4b272294aa0a2c971a3394a67535ee89e45c +size 51165 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_4_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_4_de.png index 75e7c2f592..3bbed27e43 100644 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_4_de.png +++ b/screenshots/de/features.startchat.impl.root_StartChatView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44855b0887ce9ccd5a0302c8d11ba6bf556badaefd33f897c5635d49cb44a99e -size 45071 +oid sha256:26cbf2c908b89e8cd2daa1a35cfd58dcea951e03a2ecc2334af7d8241d038254 +size 41339 diff --git a/screenshots/de/features.startchat.impl.root_StartChatView_Day_5_de.png b/screenshots/de/features.startchat.impl.root_StartChatView_Day_5_de.png deleted file mode 100644 index 1230b616fd..0000000000 --- a/screenshots/de/features.startchat.impl.root_StartChatView_Day_5_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fa14bce431debf9d9a64c4a47bed9d89730fb21d480af186dc2ac53a9e52f1af -size 31785 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_8_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_8_de.png index ebf998ba32..5b11ec067d 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_8_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa5d7bb68d549eab01750a043f0c844cf9ff2609e813fa6be5170f7e47da04f4 -size 36872 +oid sha256:35ac4bfa2e7bffba17448459c52098400f421fc3af55a7de16c24f3f293d0d42 +size 36464 diff --git a/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png b/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png index 88e163b840..5fc8b4a8fe 100644 --- a/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png +++ b/screenshots/de/libraries.matrix.ui.components_CheckableUnresolvedUserRow_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4d6fb36f1340276b3f525505e8a51245f63a1fbe8637ad33c9c3c2ef793679d -size 114204 +oid sha256:81e2d991659061d40047373c3b3aedff5a4bc2b42c2b1355448b73784891b2f5 +size 109822 diff --git a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_de.png index 74fca61a42..6b49ae7f1a 100644 --- a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9da4a0d12b26e2bf2bc3c6a0b032d462ccce782ee6ed5a62c7c32a7c2d4e0a2 -size 28400 +oid sha256:4484ff8dbba915c439270cbebab45c503d94dc82c7641a674153d88aa2aa3143 +size 28017 diff --git a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png index 6e9497454b..752973ddb4 100644 --- a/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png +++ b/screenshots/de/libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d365fb5ebb274cdf830763e8354e5fb532929f590f9ae93259c823fa66a64a5 -size 39239 +oid sha256:402ca214ecc34d4a5a5faf7583a879ab26e6c64b86904034dfa050a27a7b7a2d +size 38789 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png index 169e0d2b10..f89e8cf1c5 100644 --- a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:622e92307a1aae1a9feb420709f74d9cef063cdfd8e9f562e65655cb54ee804f -size 9991 +oid sha256:f02b4fe30d6ab57d618c925d3a110b5ca8f299717032b1380d91437acda62b7b +size 11633 diff --git a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_de.png b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_de.png index e2acbcc89c..6aee76799c 100644 --- a/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_de.png +++ b/screenshots/de/libraries.matrix.ui.components_SpaceRoomItemView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ad1db5f211ed3df04d31e973297a82685168834ee6b629ce10c3b9e82c226c9 -size 10512 +oid sha256:1dde928940515398c089e258787afca156fd1dceb9b80559c339e94e97864f12 +size 12052 diff --git a/screenshots/de/libraries.matrix.ui.components_UnresolvedUserRow_de.png b/screenshots/de/libraries.matrix.ui.components_UnresolvedUserRow_de.png index 250826b00d..25ad5c2356 100644 --- a/screenshots/de/libraries.matrix.ui.components_UnresolvedUserRow_de.png +++ b/screenshots/de/libraries.matrix.ui.components_UnresolvedUserRow_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7179346d56f1c4772169e344f1e5f01b42a5cbb86093dc6e14728f970f5fc6b -size 72739 +oid sha256:76955fb8324747c81687727bbced594f00d2c2610d91474123ecd6ced14394a4 +size 70297 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 2cd7772aff..eea6b4b636 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,99 +1,100 @@ // 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",20573,], +["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",0,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20581,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20573,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20573,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20573,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20573,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20573,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20573,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20573,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20573,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20573,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20573,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20573,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20581,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20581,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20581,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20581,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20581,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20581,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20581,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20581,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20581,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20581,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20581,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20573,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20573,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20581,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20581,], ["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",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20581,], ["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",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20573,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20573,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20573,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20573,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20573,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20573,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20573,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20573,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20573,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20573,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20581,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20581,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20581,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20581,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20581,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20581,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20581,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20581,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20581,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20573,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20573,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20573,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20573,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20573,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20581,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20581,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20581,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20581,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20581,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20573,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20573,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20573,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20573,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20581,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20581,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20581,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20581,], ["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",20573,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20581,], ["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",20573,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20581,], ["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",20573,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20581,], ["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",20573,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20581,], ["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,], @@ -103,19 +104,19 @@ 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_AttachmentsPreviewView_0_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20573,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20573,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20581,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20573,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20581,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -145,22 +146,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20573,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20581,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20573,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20573,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20581,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20573,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20573,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20573,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20573,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20573,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20581,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20581,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20581,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20581,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20581,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -171,141 +172,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20573,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20573,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20581,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20581,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20573,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20581,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20573,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20573,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20573,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20573,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20573,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20573,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20581,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20581,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20581,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20581,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20581,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20581,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20573,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20573,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20573,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20581,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20581,], ["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",20573,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20573,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20573,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20573,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20573,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20581,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20581,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20581,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20581,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20581,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20573,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20581,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20573,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20573,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20573,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20573,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20573,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20573,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20573,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20573,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20581,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20581,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20581,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20581,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20581,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20581,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20581,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20581,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20577,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20581,], ["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",20573,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20573,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20573,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20573,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20573,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20573,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20573,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20581,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20581,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20573,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20573,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20573,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20573,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20573,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20573,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20581,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20581,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20581,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20581,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20581,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20573,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20573,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20573,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20573,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20573,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20573,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20573,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20573,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20573,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20573,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20573,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20573,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20573,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20573,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20573,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20573,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20573,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20573,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20573,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20573,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20581,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20581,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20581,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20581,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20581,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20581,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20581,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20581,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20581,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20581,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20581,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20581,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20581,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20581,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20581,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20581,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20581,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20581,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20581,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20581,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20573,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20573,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20573,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20573,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20573,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20573,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20573,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20581,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20581,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20581,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20581,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20581,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20581,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20581,], ["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",20573,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20573,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20573,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20581,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20581,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20581,], ["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",20573,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20581,], ["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",20573,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20573,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20573,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20573,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20573,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20573,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20573,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20573,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20573,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20581,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20581,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20581,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20581,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20581,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20581,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20581,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20581,], ["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,], @@ -314,26 +315,24 @@ export const screenshots = [ ["libraries.designsystem.theme.components_DialogWithVeryLongTitleAndIcon_Dialog_with_a_very_long_title_and_icon_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithVeryLongTitle_Dialog_with_a_very_long_title_Dialogs_en","",0,], ["features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en","features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en",0,], -["libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en","",0,], -["libraries.designsystem.components.avatar_DmAvatars_Avatars_en","",0,], ["libraries.designsystem.text_DpScale_0_75f__en","",0,], ["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",20573,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20573,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20573,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20573,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20573,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20573,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20573,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20573,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20573,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20573,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20573,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20573,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20573,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20573,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20581,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20581,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20581,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20581,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20581,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20581,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20581,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20581,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20581,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20581,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20581,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20581,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20581,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20581,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -341,29 +340,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20573,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20573,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20581,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20581,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20573,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20573,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20573,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20573,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20573,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20573,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20573,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20573,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20573,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20577,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20581,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20581,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20581,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20581,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20581,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -383,49 +382,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20573,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20573,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20573,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20581,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20581,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20581,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20573,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20573,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20581,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20581,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20573,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20581,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20573,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20573,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20573,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20573,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20573,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20581,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20581,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20581,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20581,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20581,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20573,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20573,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20581,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20581,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20573,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20573,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20581,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20581,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20573,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20573,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20573,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20573,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20573,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20573,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20573,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20573,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20573,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20573,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20573,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20573,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20573,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20581,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20581,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20581,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20581,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20581,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20581,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20581,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20581,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20581,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20581,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20581,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20581,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20581,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -440,8 +439,8 @@ export const screenshots = [ ["appicon.element_Icon_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",20573,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20573,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20581,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20581,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -449,119 +448,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20573,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20581,], ["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",20573,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20581,], ["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",20573,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20573,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20581,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20581,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20573,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20573,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20581,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20573,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20573,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20573,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20581,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20581,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20573,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20573,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20573,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20581,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20573,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20573,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20573,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20581,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20581,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20581,], ["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",20573,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20573,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20573,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20573,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20573,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20581,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20581,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20581,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20573,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20573,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20581,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20581,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20573,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20573,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20573,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20581,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20581,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20573,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20573,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20573,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20573,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20573,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20573,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20581,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20573,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20581,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -616,45 +615,45 @@ 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",20573,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20581,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20573,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20573,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20573,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20581,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20581,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20581,], ["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",20573,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20573,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20573,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20573,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20573,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20573,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20573,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20573,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20573,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20573,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20573,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20573,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20573,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20573,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20573,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20573,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20573,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20573,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20573,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20573,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20573,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20573,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20573,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20573,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20573,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20573,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20573,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20581,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20581,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20581,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20581,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20581,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20581,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20581,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20581,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20581,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20581,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20581,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20581,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20581,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20581,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20581,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20581,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20581,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20581,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20581,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20581,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20581,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20581,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20581,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20581,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20581,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20581,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20581,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20573,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20573,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20573,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20573,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20581,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20581,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20581,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20581,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -667,26 +666,26 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20573,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20573,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20573,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20573,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20573,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20573,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20581,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20581,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20581,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20581,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20581,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20581,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20573,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20573,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20581,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -694,15 +693,20 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20573,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_19_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], @@ -712,15 +716,20 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20573,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_18_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_19_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20573,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20581,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -734,7 +743,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",20573,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20581,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -743,7 +752,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20573,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20581,], ["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,], @@ -752,23 +761,23 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20573,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20573,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20573,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20573,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20573,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20573,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20573,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20573,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20573,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20573,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20573,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20573,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20573,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20573,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20573,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20581,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20581,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20581,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20581,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20581,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20581,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20581,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20581,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20581,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20581,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20581,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20581,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20581,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20581,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20581,], ["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",20573,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20581,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -779,113 +788,117 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20573,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20573,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20573,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20581,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20581,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20581,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20573,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20573,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20581,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20573,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20581,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20573,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20573,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20581,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20573,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20573,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20573,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20573,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20573,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20573,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20581,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20581,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20581,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20581,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20581,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20581,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20573,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20573,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20584,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20584,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20581,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20584,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20584,], ["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",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20573,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20573,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20573,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20573,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20573,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20573,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20581,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20581,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20581,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20581,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20581,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20573,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20573,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20573,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20573,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20573,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20581,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20581,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20581,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20581,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20581,], ["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",20573,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20573,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20573,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20573,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20573,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20573,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20573,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20573,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20573,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20573,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20573,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20581,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20581,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20581,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20581,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20581,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20581,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20581,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20581,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20581,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20581,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20581,], ["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,], @@ -899,225 +912,225 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20573,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20573,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20581,], ["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","",20573,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20573,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20581,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20581,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20573,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20573,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20573,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20573,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20573,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20573,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20573,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20573,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20573,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20573,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20573,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20573,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20573,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20573,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20573,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20573,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20581,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20581,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20581,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20581,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20581,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20581,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20581,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20581,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20581,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20581,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20581,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20581,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20581,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20581,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20581,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20581,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20573,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20573,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20581,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20581,], ["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",20573,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20573,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20573,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20573,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20573,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20573,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20573,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20581,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20581,], ["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",20573,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20573,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20573,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20573,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20573,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20573,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20573,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20573,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20573,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20573,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20573,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20573,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20573,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20573,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20573,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20573,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20573,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20581,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20581,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20581,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20581,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20581,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20581,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20581,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20581,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20581,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20581,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20581,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20581,], ["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",20573,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20573,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20573,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20573,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20573,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20581,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20581,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20581,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20581,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20581,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20573,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20573,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20581,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20581,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20573,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20573,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20573,], -["features.roomdetails.impl_RoomDetails_0_en","",20573,], -["features.roomdetails.impl_RoomDetails_10_en","",20573,], -["features.roomdetails.impl_RoomDetails_11_en","",20573,], -["features.roomdetails.impl_RoomDetails_12_en","",20573,], -["features.roomdetails.impl_RoomDetails_13_en","",20573,], -["features.roomdetails.impl_RoomDetails_14_en","",20573,], -["features.roomdetails.impl_RoomDetails_15_en","",20573,], -["features.roomdetails.impl_RoomDetails_16_en","",20573,], -["features.roomdetails.impl_RoomDetails_17_en","",20573,], -["features.roomdetails.impl_RoomDetails_18_en","",20573,], -["features.roomdetails.impl_RoomDetails_19_en","",20573,], -["features.roomdetails.impl_RoomDetails_1_en","",20573,], -["features.roomdetails.impl_RoomDetails_20_en","",20573,], -["features.roomdetails.impl_RoomDetails_21_en","",20573,], -["features.roomdetails.impl_RoomDetails_22_en","",20573,], -["features.roomdetails.impl_RoomDetails_2_en","",20573,], -["features.roomdetails.impl_RoomDetails_3_en","",20573,], -["features.roomdetails.impl_RoomDetails_4_en","",20573,], -["features.roomdetails.impl_RoomDetails_5_en","",20573,], -["features.roomdetails.impl_RoomDetails_6_en","",20573,], -["features.roomdetails.impl_RoomDetails_7_en","",20573,], -["features.roomdetails.impl_RoomDetails_8_en","",20573,], -["features.roomdetails.impl_RoomDetails_9_en","",20573,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20573,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20573,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20573,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20573,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20573,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20573,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20573,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20573,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20573,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20581,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20581,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20581,], +["features.roomdetails.impl_RoomDetails_0_en","",20581,], +["features.roomdetails.impl_RoomDetails_10_en","",20581,], +["features.roomdetails.impl_RoomDetails_11_en","",20581,], +["features.roomdetails.impl_RoomDetails_12_en","",20581,], +["features.roomdetails.impl_RoomDetails_13_en","",20581,], +["features.roomdetails.impl_RoomDetails_14_en","",20581,], +["features.roomdetails.impl_RoomDetails_15_en","",20581,], +["features.roomdetails.impl_RoomDetails_16_en","",20581,], +["features.roomdetails.impl_RoomDetails_17_en","",20581,], +["features.roomdetails.impl_RoomDetails_18_en","",20581,], +["features.roomdetails.impl_RoomDetails_19_en","",20581,], +["features.roomdetails.impl_RoomDetails_1_en","",20581,], +["features.roomdetails.impl_RoomDetails_20_en","",20581,], +["features.roomdetails.impl_RoomDetails_21_en","",20581,], +["features.roomdetails.impl_RoomDetails_22_en","",20581,], +["features.roomdetails.impl_RoomDetails_2_en","",20581,], +["features.roomdetails.impl_RoomDetails_3_en","",20581,], +["features.roomdetails.impl_RoomDetails_4_en","",20581,], +["features.roomdetails.impl_RoomDetails_5_en","",20581,], +["features.roomdetails.impl_RoomDetails_6_en","",20581,], +["features.roomdetails.impl_RoomDetails_7_en","",20581,], +["features.roomdetails.impl_RoomDetails_8_en","",20581,], +["features.roomdetails.impl_RoomDetails_9_en","",20581,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20581,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20581,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20581,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20581,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20581,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20581,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20581,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20581,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20581,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20573,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20573,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20573,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20577,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20577,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20577,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20577,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20577,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20577,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20573,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20573,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20581,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20581,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20581,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20581,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20581,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20581,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20581,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20581,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20581,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20581,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20581,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20573,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20573,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20573,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20581,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20581,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20581,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20573,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20573,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20581,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20573,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20573,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20573,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20573,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20573,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20573,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20581,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1140,16 +1153,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20573,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20581,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20573,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20581,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1158,119 +1171,119 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20573,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20581,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20573,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20573,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20581,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20581,], ["appicon.element_RoundIcon_en","",0,], ["appicon.enterprise_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20573,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20573,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20573,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20573,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20573,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20573,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20573,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20573,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20581,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20581,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20581,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20581,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20581,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20581,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20581,], ["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","",20573,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20581,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20573,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20573,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20573,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20573,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20573,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20573,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20573,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20573,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20573,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20573,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20573,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20573,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20573,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20573,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20573,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20581,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20581,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20581,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20581,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20581,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20581,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20581,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20581,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20581,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20581,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20581,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20581,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20581,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20581,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20581,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1293,35 +1306,35 @@ 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.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20573,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20573,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20573,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20573,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20573,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20573,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20573,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20573,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20573,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20573,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20573,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20573,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20573,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20573,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20573,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20581,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20581,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20581,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20581,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20581,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20581,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20581,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20581,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20581,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20581,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20581,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20581,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20581,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20581,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20581,], ["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",20573,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20573,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20573,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20573,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20573,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20573,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20573,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20573,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20573,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20573,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20573,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20581,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20581,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20581,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20581,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20581,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20581,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20581,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20581,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20581,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20581,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20581,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1331,107 +1344,106 @@ 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",20573,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20581,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20573,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20573,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20573,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20573,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20573,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20581,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20581,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20581,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20581,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20581,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20573,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20573,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20573,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20573,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20573,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20573,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20573,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20573,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20573,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20573,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20573,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20573,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20573,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20573,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20573,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20581,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20581,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20581,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20581,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20581,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20581,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20581,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20581,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20581,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20581,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20581,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20581,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20581,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20581,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20573,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20573,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20573,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20573,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20573,], -["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20573,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20573,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20581,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20581,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20581,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20581,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20581,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20581,], ["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",20573,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20581,], ["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",20573,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20581,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20573,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20573,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20573,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20573,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20573,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20573,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20573,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20573,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20573,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20573,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20573,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20573,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20573,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20573,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20573,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20573,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20581,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20581,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20581,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20581,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20581,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20581,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20581,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20581,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20581,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20581,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20581,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20581,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20581,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20581,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20581,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20581,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20573,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20573,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20581,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20581,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1444,17 +1456,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20573,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20573,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20581,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20581,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20573,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20573,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20573,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20581,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20581,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20581,], ["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",20573,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20573,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20581,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20581,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1464,18 +1476,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20581,], ["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",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20581,], ["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,], @@ -1483,18 +1495,18 @@ 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",20573,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20581,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20573,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20573,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20581,], ["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",20573,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20581,], ["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,], @@ -1503,44 +1515,44 @@ 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",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20581,], ["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",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20581,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20581,], ["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","",20573,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20581,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20573,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20581,], ["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",20573,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20581,], ["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",20573,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20573,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20573,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20581,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20581,], ["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",20573,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20573,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20581,], ["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",20573,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20581,], ["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,], @@ -1549,8 +1561,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",20573,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20581,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20581,], ["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,], @@ -1565,8 +1577,8 @@ 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",20573,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20573,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20581,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1589,84 +1601,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20573,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20573,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20581,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20581,], ["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",20573,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20581,], ["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",20573,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20581,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20573,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20573,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20581,], ["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",20573,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20573,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20573,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20573,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20573,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20573,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20581,], ["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","",20573,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20581,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20573,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20573,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20573,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20573,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20581,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20581,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20581,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20581,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20573,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20581,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20573,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20581,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20573,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20573,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20573,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20573,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20573,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20573,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20573,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20573,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20573,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20573,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20573,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20573,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20573,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20581,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20581,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20581,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20581,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20581,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20581,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20581,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20581,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20581,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20581,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20581,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20581,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20581,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20573,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20573,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20581,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20581,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20573,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20581,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], From e49e183178d84de9e2d95b75492c2f409a8b13e9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 11 May 2026 10:19:28 +0200 Subject: [PATCH 129/471] Feature : share live location (#6741) * First live location sharing sending implementation * Simplify logic around canStop sharing * Add some debug logs around LiveLocationSharingService * Add LiveLocationException * Expose beaconId to identify the current share * Throttle live location instead of debouncing * Keep sync alive when sharing live location * Improve LiveLocation sharing * Show LiveLocationDisclaimer * Read minDistanceUpdate in LiveLocationSharingService * Set minDistanceUpdate in AdvancedSettings * Display banner in room when sharing live location * Fix tests around LiveLocationSharing * Ensure shares are properly restarted/stopped when app is re-launched * Ensure LLS data is cleared when session is removed * Update and fix LLS tests * Handle Start LLS in ui * Add check LLS permissions * Remove hardcoded strings * Fix quality and format * Create DeviceLocationProvider so we can share location data between sources (presenter/live location service) * Update screenshots * Fix warning * Do not try to stop if it was not sharing * Revert "Create DeviceLocationProvider so we can share location data between sources (presenter/live location service)" This reverts commit ba12bd968e82941cc231bdbb449310b24c97c5b8. * Tweak location provider config values * Address PR review remarks * Fix ktlint * Update screenshots * Fix some tests after merging develop * Adjust TimelineItemLocationView ui to match figma * Update screenshots * Documentation and cleanup * Remove temporary resource --------- Co-authored-by: ElementBot Co-authored-by: Benoit Marty Co-authored-by: Benoit Marty --- .../android/appnav/LoggedInFlowNode.kt | 4 +- .../android/appnav/di/SyncOrchestrator.kt | 10 +- features/location/api/build.gradle.kts | 1 + .../location/api/LiveLocationSharingBanner.kt | 100 ++++ .../live/ActiveLiveLocationShareManager.kt | 43 ++ features/location/impl/build.gradle.kts | 9 + .../impl/src/main/AndroidManifest.xml | 10 + .../impl/common/LocationConstraintsCheck.kt | 4 + .../common/SendLiveLocationPermissions.kt | 34 ++ .../common/ui/LocationConstraintsDialog.kt | 7 + .../impl/common/ui/LocationShareRow.kt | 25 +- .../impl/common/ui/UserLocationPuck.kt | 8 +- .../location/impl/di/LocationBindings.kt | 17 + .../DefaultActiveLiveLocationShareManager.kt | 227 ++++++++ .../location/impl/live/LiveLocationStore.kt | 94 ++++ .../LiveLocationSharingNotificationCreator.kt | 61 +++ .../impl/live/service/LiveLocationReceiver.kt | 14 + .../service/LiveLocationSharingCoordinator.kt | 98 ++++ .../service/LiveLocationSharingService.kt | 125 +++++ .../location/impl/share/ShareLocationEvent.kt | 3 +- .../impl/share/ShareLocationPresenter.kt | 62 ++- .../location/impl/share/ShareLocationState.kt | 3 + .../impl/share/ShareLocationStateProvider.kt | 15 + .../location/impl/share/ShareLocationView.kt | 58 ++- .../location/impl/show/ShowLocationEvent.kt | 1 + .../impl/show/ShowLocationPresenter.kt | 13 +- .../location/impl/show/ShowLocationState.kt | 5 +- .../impl/show/ShowLocationStateProvider.kt | 2 + .../location/impl/show/ShowLocationView.kt | 1 + .../common/LocationConstraintsCheckTest.kt | 22 +- ...faultActiveLiveLocationShareManagerTest.kt | 488 ++++++++++++++++++ .../LiveLocationSharingCoordinatorTest.kt | 115 +++++ .../DefaultShareLocationEntryPointTest.kt | 14 +- .../impl/share/ShareLocationPresenterTest.kt | 247 ++++++++- .../impl/share/ShareLocationViewTest.kt | 32 ++ .../show/DefaultShowLocationEntryPointTest.kt | 5 +- .../show/LiveLocationShareComparatorTest.kt | 14 +- .../impl/show/ShowLocationPresenterTest.kt | 27 +- .../impl/store/LiveLocationStoreTest.kt | 129 +++++ .../FakeActiveLiveLocationShareManager.kt | 46 ++ .../features/messages/impl/MessagesEvent.kt | 2 + .../messages/impl/MessagesFlowNode.kt | 8 + .../messages/impl/MessagesNavigator.kt | 1 + .../features/messages/impl/MessagesNode.kt | 5 + .../messages/impl/MessagesPresenter.kt | 18 + .../features/messages/impl/MessagesState.kt | 1 + .../messages/impl/MessagesStateProvider.kt | 3 + .../features/messages/impl/MessagesView.kt | 82 +-- .../actionlist/ActionListStateProvider.kt | 5 +- .../impl/threads/ThreadedMessagesNode.kt | 6 + .../impl/timeline/TimelinePresenter.kt | 6 +- .../components/TimelineItemEventRow.kt | 2 +- .../event/TimelineItemLocationView.kt | 17 +- .../di/FakeTimelineItemPresenterFactories.kt | 8 + .../event/TimelineItemContentFactory.kt | 1 + .../event/TimelineItemEventContentProvider.kt | 4 +- .../event/TimelineItemLocationContent.kt | 3 +- .../TimelineItemLocationContentProvider.kt | 50 +- .../messages/impl/FakeMessagesNavigator.kt | 5 + .../messages/impl/MessagesPresenterTest.kt | 37 ++ .../messages/impl/MessagesViewTest.kt | 38 ++ .../impl/timeline/TimelinePresenterTest.kt | 3 + .../impl/advanced/AdvancedSettingsEvents.kt | 1 + .../impl/advanced/AdvancedSettingsNode.kt | 6 +- .../advanced/AdvancedSettingsPresenter.kt | 20 + .../impl/advanced/AdvancedSettingsState.kt | 1 + .../advanced/AdvancedSettingsStateProvider.kt | 2 + .../impl/advanced/AdvancedSettingsView.kt | 97 +++- .../advanced/AdvancedSettingsPresenterTest.kt | 69 ++- .../impl/advanced/AdvancedSettingsViewTest.kt | 2 + .../libraries/matrix/api/MatrixClient.kt | 2 + .../matrix/api/room/location/BeaconId.kt | 12 + .../api/room/location/BeaconInfoUpdate.kt | 16 + .../room/location/LiveLocationException.kt | 14 + .../api/room/location/LiveLocationShare.kt | 2 + .../libraries/matrix/impl/RustMatrixClient.kt | 12 + .../matrix/impl/room/JoinedRustRoom.kt | 12 + .../impl/room/location/BeaconInfoUpdates.kt | 21 + .../room/location/LiveLocationException.kt | 19 + .../room/location/LiveLocationSharesFlow.kt | 10 +- .../TimedLiveLocationSharesFlowTest.kt | 27 +- .../libraries/matrix/test/FakeMatrixClient.kt | 2 + .../matrix/test/room/FakeJoinedRoom.kt | 2 +- .../test/room/location/LiveLocationFixture.kt | 38 ++ .../api/store/AppPreferencesStore.kt | 3 + libraries/preferences/impl/build.gradle.kts | 3 + .../impl/store/DefaultAppPreferencesStore.kt | 14 + .../store/DefaultAppPreferencesStoreTest.kt | 57 ++ .../test/InMemoryAppPreferencesStore.kt | 10 + .../notifications/NotificationIdProvider.kt | 1 + .../test/observer/FakeSessionObserver.kt | 5 +- .../api/AppForegroundStateService.kt | 4 + .../impl/DefaultAppForegroundStateService.kt | 6 + .../test/FakeAppForegroundStateService.kt | 7 + ...api_LiveLocationSharingBanner_Day_0_en.png | 3 + ...i_LiveLocationSharingBanner_Night_0_en.png | 3 + ...pl.common.ui_LocationShareRow_Day_0_en.png | 4 +- ....common.ui_LocationShareRow_Night_0_en.png | 4 +- ....impl.share_ShareLocationView_Day_6_en.png | 4 +- ....impl.share_ShareLocationView_Day_7_en.png | 3 + ....impl.share_ShareLocationView_Day_8_en.png | 3 + ...mpl.share_ShareLocationView_Night_6_en.png | 4 +- ...mpl.share_ShareLocationView_Night_7_en.png | 3 + ...mpl.share_ShareLocationView_Night_8_en.png | 3 + ...vent_TimelineItemLocationView_Day_1_en.png | 4 +- ...vent_TimelineItemLocationView_Day_2_en.png | 4 +- ...vent_TimelineItemLocationView_Day_3_en.png | 4 +- ...nt_TimelineItemLocationView_Night_1_en.png | 4 +- ...nt_TimelineItemLocationView_Night_2_en.png | 4 +- ...nt_TimelineItemLocationView_Night_3_en.png | 4 +- ...s.messages.impl_MessagesView_Day_10_en.png | 4 +- ...s.messages.impl_MessagesView_Day_11_en.png | 3 + ...es.messages.impl_MessagesView_Day_8_en.png | 4 +- ...es.messages.impl_MessagesView_Day_9_en.png | 4 +- ...messages.impl_MessagesView_Night_10_en.png | 4 +- ...messages.impl_MessagesView_Night_11_en.png | 3 + ....messages.impl_MessagesView_Night_8_en.png | 4 +- ....messages.impl_MessagesView_Night_9_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_0_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_1_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_2_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_3_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_4_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_5_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_6_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_7_en.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_8_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_0_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_1_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_2_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_3_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_4_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_5_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_6_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_7_en.png | 4 +- ...advanced_AdvancedSettingsViewDark_8_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_0_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_1_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_2_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_3_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_4_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_5_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_6_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_7_en.png | 4 +- ...dvanced_AdvancedSettingsViewLight_8_en.png | 4 +- 145 files changed, 2913 insertions(+), 278 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/LiveLocationSharingBanner.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/SendLiveLocationPermissions.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/di/LocationBindings.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/LiveLocationStore.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/notification/LiveLocationSharingNotificationCreator.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManagerTest.kt create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/store/LiveLocationStoreTest.kt create mode 100644 features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeActiveLiveLocationShareManager.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconId.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconInfoUpdate.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationException.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/BeaconInfoUpdates.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationException.kt create mode 100644 libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/location/LiveLocationFixture.kt create mode 100644 libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt create mode 100644 tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 70376ffcb9..f2120038a6 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -54,6 +54,7 @@ import io.element.android.features.ftue.api.state.FtueService import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer @@ -151,6 +152,7 @@ class LoggedInFlowNode( private val analyticsService: AnalyticsService, private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher, private val createRoomEntryPoint: CreateRoomEntryPoint, + private val activeLiveLocationShareManager: ActiveLiveLocationShareManager, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Placeholder, @@ -211,6 +213,7 @@ class LoggedInFlowNode( super.onBuilt() lifecycleScope.launch { sessionEnterpriseService.init() + activeLiveLocationShareManager.setup() } lifecycle.subscribe( onCreate = { @@ -219,7 +222,6 @@ class LoggedInFlowNode( loggedInFlowProcessor.observeEvents(sessionCoroutineScope) matrixClient.sessionVerificationService.setListener(verificationListener) mediaPreviewConfigMigration() - sessionCoroutineScope.launch { // Wait for the network to be connected before pre-fetching the max file upload size networkMonitor.connectivity.first { networkStatus -> networkStatus == NetworkStatus.Connected } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt index 9b1bbd1b81..1ce423a569 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/SyncOrchestrator.kt @@ -89,16 +89,14 @@ class SyncOrchestrator( @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal fun observeStates() = coroutineScope.launch { Timber.tag(tag).d("start observing the app and network state") - - val isAppActiveFlow = combine( + val isAppActiveFlows = listOf( appForegroundStateService.isInForeground, appForegroundStateService.isInCall, appForegroundStateService.isSyncingNotificationEvent, appForegroundStateService.hasRingingCall, - ) { isInForeground, isInCall, isSyncingNotificationEvent, hasRingingCall -> - isInForeground || isInCall || isSyncingNotificationEvent || hasRingingCall - } - + appForegroundStateService.isSharingLiveLocation + ) + val isAppActiveFlow = combine(isAppActiveFlows) { actives -> actives.any { it } } combine( // small debounce to avoid spamming startSync when the state is changing quickly in case of error. syncService.syncState.debounce(100.milliseconds), diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index ab85e37594..f8377389f1 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -71,6 +71,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) implementation(libs.coil.compose) + implementation(libs.datetime) testCommonDependencies(libs) } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/LiveLocationSharingBanner.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LiveLocationSharingBanner.kt new file mode 100644 index 0000000000..8c53550737 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/LiveLocationSharingBanner.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.api + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +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.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.ButtonSize +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 +fun LiveLocationSharingBanner( + onClick: () -> Unit, + onStopClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .background(ElementTheme.colors.bgCanvasDefault) + .drawBannerBorder(ElementTheme.colors.separatorPrimary) + .clickable(onClick = onClick) + .padding(horizontal = 16.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = CompoundIcons.LocationPinSolid(), + contentDescription = null, + tint = ElementTheme.colors.iconAccentPrimary, + modifier = Modifier.size(24.dp), + ) + Text( + text = stringResource(CommonStrings.screen_room_live_location_banner), + style = ElementTheme.typography.fontBodyMdMedium, + color = ElementTheme.colors.textPrimary, + ) + } + Button( + text = stringResource(CommonStrings.action_stop), + onClick = onStopClick, + destructive = true, + size = ButtonSize.Small, + ) + } +} + +private fun Modifier.drawBannerBorder(borderColor: Color): Modifier = drawBehind { + val strokeWidth = 1.dp.toPx() + val bottomY = size.height - strokeWidth / 2 + drawLine( + color = borderColor, + start = Offset(0f, strokeWidth / 2), + end = Offset(size.width, strokeWidth / 2), + strokeWidth = strokeWidth, + ) + drawLine( + color = borderColor, + start = Offset(0f, bottomY), + end = Offset(size.width, bottomY), + strokeWidth = strokeWidth, + ) +} + +@PreviewsDayNight +@Composable +internal fun LiveLocationSharingBannerPreview() = ElementPreview { + LiveLocationSharingBanner( + onClick = {}, + onStopClick = {}, + ) +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt new file mode 100644 index 0000000000..cd6b8731c1 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/live/ActiveLiveLocationShareManager.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.api.live + +import io.element.android.libraries.core.coroutine.mapState +import io.element.android.libraries.matrix.api.core.RoomId +import kotlinx.coroutines.flow.StateFlow +import kotlin.time.Duration + +interface ActiveLiveLocationShareManager { + /** All rooms currently sharing live location on this device. */ + val sharingRoomIds: StateFlow> + + /** + * Initializes the manager. + * This will restart or stop current location sharing and set the listener on the SDK + * and the session manager. + */ + suspend fun setup() + + /** + * Starts live location sharing in the given room. + * Calls room.startLiveLocationShare() on the SDK, registers the share, + * and starts the foreground GPS service if not already running. + */ + suspend fun startShare(roomId: RoomId, duration: Duration): Result + + /** + * Stops live location sharing in the given room. + * Calls room.stopLiveLocationShare() on the SDK, removes the share, + * and stops the foreground service if no shares remain. + */ + suspend fun stopShare(roomId: RoomId): Result +} + +fun ActiveLiveLocationShareManager.isCurrentlySharing(roomId: RoomId): StateFlow { + return sharingRoomIds.mapState { roomId in it } +} diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts index 0da54a1394..165c32b7c5 100644 --- a/features/location/impl/build.gradle.kts +++ b/features/location/impl/build.gradle.kts @@ -37,10 +37,16 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.matrixui) implementation(projects.services.analytics.api) + implementation(projects.services.appnavstate.api) implementation(libs.accompanist.permission) implementation(projects.libraries.uiStrings) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.dateformatter.api) + implementation(projects.libraries.preferences.api) + implementation(projects.libraries.push.api) + implementation(projects.libraries.sessionStorage.api) + implementation(libs.androidx.datastore.preferences) + implementation(libs.datetime) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) @@ -50,4 +56,7 @@ dependencies { testImplementation(projects.services.analytics.test) testImplementation(projects.features.messages.test) testImplementation(projects.libraries.featureflag.test) + testImplementation(projects.libraries.preferences.test) + testImplementation(projects.libraries.sessionStorage.test) + testImplementation(projects.features.location.test) } diff --git a/features/location/impl/src/main/AndroidManifest.xml b/features/location/impl/src/main/AndroidManifest.xml index ae728c09e1..e92ca68077 100644 --- a/features/location/impl/src/main/AndroidManifest.xml +++ b/features/location/impl/src/main/AndroidManifest.xml @@ -9,4 +9,14 @@ + + + + + + + diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheck.kt index a0b0cd4734..f90793b775 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheck.kt @@ -16,13 +16,16 @@ sealed interface LocationConstraintsCheck { data object PermissionRationale : LocationConstraintsCheck data object PermissionDenied : LocationConstraintsCheck data object LocationServiceDisabled : LocationConstraintsCheck + data object NotEnoughPowerLevel : LocationConstraintsCheck } fun checkLocationConstraints( permissionsState: PermissionsState, locationActions: LocationActions, + sendLiveLocationPermissions: SendLiveLocationPermissions, ): LocationConstraintsCheck { return when { + !sendLiveLocationPermissions.hasAll -> LocationConstraintsCheck.NotEnoughPowerLevel permissionsState.isAnyGranted -> { if (locationActions.isLocationEnabled()) { LocationConstraintsCheck.Success @@ -41,5 +44,6 @@ fun LocationConstraintsCheck.toDialogState(): LocationConstraintsDialogState { LocationConstraintsCheck.PermissionRationale -> LocationConstraintsDialogState.PermissionRationale LocationConstraintsCheck.PermissionDenied -> LocationConstraintsDialogState.PermissionDenied LocationConstraintsCheck.LocationServiceDisabled -> LocationConstraintsDialogState.LocationServiceDisabled + LocationConstraintsCheck.NotEnoughPowerLevel -> LocationConstraintsDialogState.NotEnoughPowerLevel } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/SendLiveLocationPermissions.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/SendLiveLocationPermissions.kt new file mode 100644 index 0000000000..d1a9e32026 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/SendLiveLocationPermissions.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common + +import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.room.StateEventType +import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions + +/** + * Permissions to send beacon and beacon_info events in the room. + */ +data class SendLiveLocationPermissions( + val canSendBeacon: Boolean, + val canSendBeaconInfo: Boolean, +) { + val hasAll = canSendBeaconInfo && canSendBeacon + + companion object { + val DEFAULT = SendLiveLocationPermissions(canSendBeacon = false, canSendBeaconInfo = false) + val GRANTED = SendLiveLocationPermissions(canSendBeacon = true, canSendBeaconInfo = true) + } +} + +fun RoomPermissions.sendLiveLocationPermissions(): SendLiveLocationPermissions { + return SendLiveLocationPermissions( + canSendBeaconInfo = canOwnUserSendState(StateEventType.BeaconInfo), + canSendBeacon = canOwnUserSendMessage(MessageEventType.Beacon), + ) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationConstraintsDialog.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationConstraintsDialog.kt index 95f5129f91..334aebaee6 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationConstraintsDialog.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationConstraintsDialog.kt @@ -10,6 +10,8 @@ package io.element.android.features.location.impl.common.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.ui.res.stringResource +import io.element.android.features.location.impl.R +import io.element.android.libraries.designsystem.components.dialogs.AlertDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.ui.strings.CommonStrings @@ -42,6 +44,10 @@ fun LocationConstraintsDialog( onDismiss = onDismiss, submitText = stringResource(CommonStrings.action_continue), ) + LocationConstraintsDialogState.NotEnoughPowerLevel -> AlertDialog( + content = stringResource(R.string.screen_share_location_live_location_missing_permissions), + onDismiss = onDismiss + ) } } @@ -51,4 +57,5 @@ sealed interface LocationConstraintsDialogState { data object PermissionRationale : LocationConstraintsDialogState data object PermissionDenied : LocationConstraintsDialogState data object LocationServiceDisabled : LocationConstraintsDialogState + data object NotEnoughPowerLevel : LocationConstraintsDialogState } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt index 3d7b8df618..24476e3c66 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationShareRow.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.material3.IconButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -44,6 +45,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun LocationShareRow( item: LocationShareItem, onShareClick: () -> Unit, + onStopClick: () -> Unit, modifier: Modifier = Modifier, ) { Row( @@ -101,11 +103,24 @@ fun LocationShareRow( ) } } + if (item.canStopSharing) { + IconButton( + onClick = onStopClick, + colors = IconButtonDefaults.iconButtonColors( + containerColor = ElementTheme.colors.bgCriticalPrimary, + contentColor = ElementTheme.colors.iconOnSolidPrimary, + ) + ) { + Icon( + imageVector = CompoundIcons.Stop(), + contentDescription = stringResource(CommonStrings.action_stop), + ) + } + } IconButton(onClick = onShareClick) { Icon( imageVector = CompoundIcons.ShareAndroid(), contentDescription = stringResource(CommonStrings.action_share), - tint = ElementTheme.colors.iconPrimary, ) } } @@ -128,8 +143,10 @@ internal fun LocationShareRowPreview() = ElementPreview { formattedTimestamp = "Shared 1 min ago", isLive = true, assetType = AssetType.SENDER, - location = Location(0.0, 0.0) + location = Location(0.0, 0.0), + isOwnUser = true, ), + onStopClick = {}, onShareClick = {}, ) LocationShareRow( @@ -145,8 +162,10 @@ internal fun LocationShareRowPreview() = ElementPreview { isLive = false, assetType = AssetType.PIN, formattedTimestamp = "Shared 5 hours ago", - location = Location(0.0, 0.0) + location = Location(0.0, 0.0), + isOwnUser = false ), + onStopClick = {}, onShareClick = {}, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt index 8b89f77be4..589ed87c6f 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt @@ -23,7 +23,7 @@ import org.maplibre.compose.location.UserLocationState import org.maplibre.compose.location.rememberAndroidLocationProvider import org.maplibre.compose.location.rememberNullLocationProvider import org.maplibre.compose.location.rememberUserLocationState -import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds @Composable fun UserLocationPuck( @@ -72,9 +72,9 @@ fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState rememberNullLocationProvider() } else { rememberAndroidLocationProvider( - updateInterval = 1.minutes, - desiredAccuracy = DesiredAccuracy.Balanced, - minDistanceMeters = 50f, + updateInterval = 5.seconds, + desiredAccuracy = DesiredAccuracy.High, + minDistanceMeters = 5f, ) } return rememberUserLocationState(locationProvider) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/di/LocationBindings.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/di/LocationBindings.kt new file mode 100644 index 0000000000..ee70936160 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/di/LocationBindings.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.di + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesTo +import io.element.android.features.location.impl.live.service.LiveLocationSharingService + +@ContributesTo(AppScope::class) +interface LocationBindings { + fun inject(service: LiveLocationSharingService) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt new file mode 100644 index 0000000000..fd16bea515 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live + +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.SingleIn +import dev.zacsweers.metro.binding +import io.element.android.features.location.api.Location +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager +import io.element.android.features.location.impl.live.service.LiveLocationReceiver +import io.element.android.features.location.impl.live.service.LiveLocationSharingCoordinator +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.location.BeaconId +import io.element.android.libraries.matrix.api.room.location.LiveLocationException +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.coroutines.Job +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.getAndUpdate +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.util.concurrent.ConcurrentHashMap +import kotlin.concurrent.atomics.AtomicBoolean +import kotlin.concurrent.atomics.ExperimentalAtomicApi +import kotlin.time.Duration +import kotlin.time.Instant + +@OptIn(ExperimentalAtomicApi::class) +@SingleIn(SessionScope::class) +@ContributesBinding(SessionScope::class, binding = binding()) +class DefaultActiveLiveLocationShareManager( + private val matrixClient: MatrixClient, + private val coordinator: LiveLocationSharingCoordinator, + private val liveLocationStore: LiveLocationStore, + private val clock: SystemClock, + private val sessionObserver: SessionObserver, +) : ActiveLiveLocationShareManager, LiveLocationReceiver { + private val isSetup = AtomicBoolean(false) + private val cachedRooms = ConcurrentHashMap() + private val timeoutJobs = ConcurrentHashMap() + private val syncedActiveShareIds = MutableStateFlow>(emptySet()) + private val localSharingRoomIds = MutableStateFlow>(emptySet()) + override val sharingRoomIds: StateFlow> = localSharingRoomIds + + override suspend fun setup() = withContext(NonCancellable) { + if (isSetup.compareAndSet(expectedValue = false, newValue = true)) { + Timber.d("ActiveLiveLocationShareManager setup manager.") + + recoverPersistedShares() + + matrixClient.ownBeaconInfoUpdates + .onEach { update -> + Timber.d("Received beaconInfoUpdate:$update") + // First cancel the local share in this room if any. + if (update.roomId in localSharingRoomIds.value) { + stopLocalShare(roomId = update.roomId) + } + syncedActiveShareIds.update { + if (update.isLive) { + it + update.beaconId + } else { + it - update.beaconId + } + } + } + .launchIn(matrixClient.sessionCoroutineScope) + + sessionObserver.addListener(sessionListener) + } + } + + private val sessionListener: SessionListener = object : SessionListener { + override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { + if (matrixClient.sessionId.value == userId) { + clear() + } + } + } + + override suspend fun startShare(roomId: RoomId, duration: Duration): Result = withContext(NonCancellable) { + Timber.d("ActiveLiveLocationShareManager starting share for room $roomId with duration ${duration.inWholeSeconds}s") + val room = cachedRooms.getOrPut(roomId) { + matrixClient.getJoinedRoom(roomId) ?: return@withContext Result.failure(IllegalStateException("No room found for $roomId")) + } + // Before starting a new location share, stop the current one if any is active. + room.stopLiveLocationShare() + + room.startLiveLocationShare(duration.inWholeMilliseconds) + .onSuccess { beaconId -> + Timber.d("ActiveLiveLocationShareManager wait remote echo of $beaconId") + syncedActiveShareIds.first { beaconIds -> beaconIds.contains(beaconId) } + val expiresAt = Instant.fromEpochMilliseconds(clock.epochMillis() + duration.inWholeMilliseconds) + startLocalShare(roomId, expiresAt) + } + .onFailure { + Timber.e(it, "ActiveLiveLocationShareManager failed to start share for room $roomId") + stopLocalShare(roomId) + } + .map { } + } + + override suspend fun stopShare(roomId: RoomId): Result = withContext(NonCancellable) { + Timber.d("ActiveLiveLocationShareManager stopping share for room $roomId") + val room = cachedRooms.getOrPut(roomId) { + matrixClient.getJoinedRoom(roomId) ?: return@withContext Result.failure(IllegalStateException("No room found for $roomId")) + } + room.stopLiveLocationShare() + .onSuccess { + Timber.d("ActiveLiveLocationShareManager share stopped successfully for room $roomId") + } + .onFailure { + Timber.e(it, "ActiveLiveLocationShareManager failed to stop share for room $roomId") + } + .also { + stopLocalShare(roomId) + } + } + + override suspend fun onLocationUpdate(location: Location) { + val activeSharesCount = localSharingRoomIds.value.size + Timber.d("ActiveLiveLocationShareManager received location update for $activeSharesCount active share(s)") + localSharingRoomIds.value.forEach { roomId -> + Timber.d("ActiveLiveLocationShareManager sending location to room $roomId") + sendLiveLocation(roomId, location) + .onFailure { + Timber.e(it, "ActiveLiveLocationShareManager failed to send location to room $roomId") + } + } + } + + private suspend fun sendLiveLocation(roomId: RoomId, location: Location): Result { + val room = cachedRooms.getOrPut(roomId) { + matrixClient.getJoinedRoom(roomId) ?: return Result.failure(IllegalStateException("No room found for $roomId")) + } + return room.sendLiveLocation(location.toGeoUri()) + .recoverCatching { exception -> + when (exception) { + is LiveLocationException.NotLive -> { + stopLocalShare(roomId) + throw exception + } + else -> throw exception + } + } + } + + private suspend fun startLocalShare(roomId: RoomId, expiresAt: Instant) { + val wasEmpty = localSharingRoomIds.value.isEmpty() + Timber.d("ActiveLiveLocationShareManager share started successfully for room $roomId (wasEmpty=$wasEmpty)") + localSharingRoomIds.update { it + roomId } + liveLocationStore.setLiveLocationExpiry(roomId, expiresAt) + scheduleTimeout(roomId, expiresAt) + if (wasEmpty) { + Timber.d("ActiveLiveLocationShareManager registering with coordinator for session ${matrixClient.sessionId}") + coordinator.register(matrixClient.sessionId, this@DefaultActiveLiveLocationShareManager) + } + } + + private suspend fun recoverPersistedShares() { + val now = Instant.fromEpochMilliseconds(clock.epochMillis()) + liveLocationStore.getLiveLocationExpiries().forEach { (roomId, expiresAt) -> + if (expiresAt > now) { + // Only starts locally as the share is already started remotely + startLocalShare(roomId, expiresAt) + } else { + // Explicitly stop the share on the server. + stopShare(roomId) + } + } + } + + private fun scheduleTimeout(roomId: RoomId, expiresAt: Instant) { + timeoutJobs.remove(roomId)?.cancel() + val delayMillis = expiresAt.toEpochMilliseconds() - clock.epochMillis() + timeoutJobs[roomId] = matrixClient.sessionCoroutineScope.launch { + delay(delayMillis) + stopShare(roomId) + .onFailure { error -> + Timber.e(error, "ActiveLiveLocationShareManager failed to stop timed out share for room $roomId") + } + } + } + + private suspend fun stopLocalShare(roomId: RoomId) { + Timber.d("ActiveLiveLocationShareManager stop local share in $roomId") + timeoutJobs.remove(roomId)?.cancel() + val wasSharing = localSharingRoomIds.getAndUpdate { it - roomId }.isNotEmpty() + cachedRooms.remove(roomId)?.close() + liveLocationStore.removeLiveLocationExpiry(roomId) + if (wasSharing && localSharingRoomIds.value.isEmpty()) { + Timber.d("ActiveLiveLocationShareManager unregistering from coordinator for session ${matrixClient.sessionId}") + coordinator.unregister(matrixClient.sessionId) + } + } + + private suspend fun clear() { + Timber.d("ActiveLiveLocationShareManager clear state") + sessionObserver.removeListener(sessionListener) + coordinator.unregister(matrixClient.sessionId) + liveLocationStore.clear() + for (room in cachedRooms.values) { + room.close() + timeoutJobs[room.roomId]?.cancel() + } + timeoutJobs.clear() + cachedRooms.clear() + localSharingRoomIds.value = emptySet() + syncedActiveShareIds.value = emptySet() + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/LiveLocationStore.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/LiveLocationStore.kt new file mode 100644 index 0000000000..417d9d423a --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/LiveLocationStore.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live + +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringSetPreferencesKey +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn +import io.element.android.libraries.androidutils.hash.hash +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory +import kotlinx.coroutines.flow.first +import timber.log.Timber +import kotlin.time.Instant + +private const val LIVE_LOCATION_EXPIRY_VALUE_SEPARATOR = "=" + +@Inject +@SingleIn(SessionScope::class) +class LiveLocationStore( + preferenceDataStoreFactory: PreferenceDataStoreFactory, + sessionId: SessionId, +) { + private val store = preferenceDataStoreFactory.create("location_${sessionId.value.hash().take(16)}") + private val acceptedLiveLocationDisclaimerKey = booleanPreferencesKey("live_location_disclaimer_accepted") + private val liveLocationExpiriesKey = stringSetPreferencesKey("live_location_expiries") + + suspend fun hasAcceptedLiveLocationDisclaimer(): Boolean = runCatchingExceptions { + store.data.first()[acceptedLiveLocationDisclaimerKey] ?: false + }.getOrDefault(false) + + suspend fun setAcceptedLiveLocationDisclaimer(): Result = runCatchingExceptions { + store.edit { prefs -> + prefs[acceptedLiveLocationDisclaimerKey] = true + } + } + + suspend fun getLiveLocationExpiries(): Map = runCatchingExceptions { + val serialized = store.data.first()[liveLocationExpiriesKey].orEmpty() + decodeLiveLocationExpiries(serialized) + }.onFailure { error -> + Timber.e(error, "Failed to decode live location expiry payload") + }.getOrDefault(emptyMap()) + + suspend fun setLiveLocationExpiry(roomId: RoomId, expiresAt: Instant): Result = runCatchingExceptions { + store.edit { prefs -> + val current = decodeLiveLocationExpiries(prefs[liveLocationExpiriesKey].orEmpty()) + prefs[liveLocationExpiriesKey] = encodeLiveLocationExpiries(current + (roomId to expiresAt)) + } + } + + suspend fun removeLiveLocationExpiry(roomId: RoomId): Result = runCatchingExceptions { + store.edit { prefs -> + val current = decodeLiveLocationExpiries(prefs[liveLocationExpiriesKey].orEmpty()) + val updated = current - roomId + if (updated.isEmpty()) { + prefs.remove(liveLocationExpiriesKey) + } else { + prefs[liveLocationExpiriesKey] = encodeLiveLocationExpiries(updated) + } + } + } + + private fun decodeLiveLocationExpiries(serialized: Set): Map { + return runCatchingExceptions { + serialized + .map { it.split(LIVE_LOCATION_EXPIRY_VALUE_SEPARATOR) } + .associate { values -> + val roomId = RoomId(values[0]) + val expiresAtMillis = values[1].toLong() + roomId to Instant.fromEpochMilliseconds(expiresAtMillis) + } + }.getOrDefault(emptyMap()) + } + + private fun encodeLiveLocationExpiries(expiries: Map): Set { + return expiries.entries.map { (roomId, expiresAt) -> + "${roomId.value}$LIVE_LOCATION_EXPIRY_VALUE_SEPARATOR${expiresAt.toEpochMilliseconds()}" + }.toSet() + } + + suspend fun clear() { + store.edit { prefs -> prefs.clear() } + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/notification/LiveLocationSharingNotificationCreator.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/notification/LiveLocationSharingNotificationCreator.kt new file mode 100644 index 0000000000..9d4c461b0e --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/notification/LiveLocationSharingNotificationCreator.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live.notification + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import dev.zacsweers.metro.Inject +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.ui.strings.CommonStrings + +@Inject +class LiveLocationSharingNotificationCreator( + @ApplicationContext private val context: Context, + private val buildMeta: BuildMeta, +) { + companion object { + const val CHANNEL_ID = "LIVE_LOCATION_SHARING" + } + + fun createNotification(): Notification { + if (supportNotificationChannels()) { + ensureChannelExists() + } + return NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(android.R.drawable.ic_menu_mylocation) + .setContentTitle(context.getString(CommonStrings.live_location_sharing_foreground_service_title_android, buildMeta.applicationName)) + .setContentText(context.getString(CommonStrings.live_location_sharing_foreground_service_message_android)) + .setOngoing(true) + .build() + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun ensureChannelExists() { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) { + notificationManager.createNotificationChannel( + NotificationChannel( + CHANNEL_ID, + context.getString(CommonStrings.live_location_sharing_foreground_service_channel_title_android) + .ifEmpty { "Live Location Sharing" }, + NotificationManager.IMPORTANCE_LOW, + ) + ) + } + } + + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) + private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt new file mode 100644 index 0000000000..adba75730c --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live.service + +import io.element.android.features.location.api.Location + +fun interface LiveLocationReceiver { + suspend fun onLocationUpdate(location: Location) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt new file mode 100644 index 0000000000..e39acb14e8 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live.service + +import android.content.Context +import android.content.Intent +import androidx.core.content.ContextCompat +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.SingleIn +import io.element.android.features.location.api.Location +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.services.toolbox.api.systemclock.SystemClock +import timber.log.Timber +import java.util.concurrent.ConcurrentHashMap +import kotlin.concurrent.atomics.AtomicLong +import kotlin.concurrent.atomics.AtomicReference +import kotlin.concurrent.atomics.ExperimentalAtomicApi +import kotlin.time.Duration.Companion.seconds + +private val THROTTLE_WINDOW = 3.seconds + +@OptIn(ExperimentalAtomicApi::class) +@SingleIn(AppScope::class) +class LiveLocationSharingCoordinator internal constructor( + private val startService: () -> Unit, + private val stopService: () -> Unit, + private val nowMillis: () -> Long, +) { + @Inject + constructor(@ApplicationContext context: Context, clock: SystemClock) : this( + startService = { + ContextCompat.startForegroundService(context, Intent(context, LiveLocationSharingService::class.java)) + }, + stopService = { + context.stopService(Intent(context, LiveLocationSharingService::class.java)) + }, + nowMillis = clock::epochMillis + ) + + private val receivers = ConcurrentHashMap() + + private val lastDispatchMillis = AtomicLong(0L) + private val lastKnownLocation = AtomicReference(null) + + suspend fun register(sessionId: SessionId, receiver: LiveLocationReceiver) { + val wasEmpty = receivers.isEmpty() + Timber.d("LiveLocationSharingCoordinator registering receiver for session $sessionId (wasEmpty=$wasEmpty)") + receivers[sessionId] = receiver + if (wasEmpty) { + Timber.d("LiveLocationSharingCoordinator starting service") + runCatchingExceptions(startService).onFailure { + Timber.e(it, "Failed to start live location sharing service") + } + } + lastKnownLocation.load()?.let { + dispatch(it) + } + } + + fun unregister(sessionId: SessionId) { + Timber.d("LiveLocationSharingCoordinator unregistering receiver for session $sessionId") + receivers.remove(sessionId) + if (receivers.isEmpty()) { + lastKnownLocation.store(null) + Timber.d("LiveLocationSharingCoordinator stopping service (no more receivers)") + runCatchingExceptions(stopService).onFailure { + Timber.e(it, "Failed to stop live location sharing service") + } + } + } + + suspend fun dispatch(location: Location) { + val currentTimeMillis = nowMillis() + val millisSincePrevious = currentTimeMillis - lastDispatchMillis.load() + if (millisSincePrevious < THROTTLE_WINDOW.inWholeMilliseconds) { + Timber.d("Received location before $THROTTLE_WINDOW, ignore.") + return + } + lastKnownLocation.store(location) + lastDispatchMillis.store(currentTimeMillis) + receivers.forEach { (sessionId, receiver) -> + Timber.d("Dispatch received location for session $sessionId ") + runCatchingExceptions { + receiver.onLocationUpdate(location) + }.onFailure { + Timber.e(it, "Failed to dispatch live location update for session $sessionId") + } + } + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt new file mode 100644 index 0000000000..4451febb19 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live.service + +import android.annotation.SuppressLint +import android.app.Service +import android.content.Intent +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION +import android.os.IBinder +import androidx.core.app.ServiceCompat +import dev.zacsweers.metro.Inject +import io.element.android.features.location.impl.di.LocationBindings +import io.element.android.features.location.impl.live.notification.LiveLocationSharingNotificationCreator +import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.core.coroutine.childScope +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.annotations.AppCoroutineScope +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.push.api.notifications.ForegroundServiceType +import io.element.android.libraries.push.api.notifications.NotificationIdProvider +import io.element.android.services.appnavstate.api.AppForegroundStateService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import org.maplibre.compose.location.AndroidLocationProvider +import org.maplibre.compose.location.DesiredAccuracy +import timber.log.Timber +import kotlin.time.Duration.Companion.seconds +import io.element.android.features.location.api.Location as ApiLocation + +private const val UPDATE_INTERVAL_IN_SECOND = 10 + +class LiveLocationSharingService : Service() { + @Inject lateinit var coordinator: LiveLocationSharingCoordinator + @Inject lateinit var notificationCreator: LiveLocationSharingNotificationCreator + @Inject lateinit var appPreferencesStore: AppPreferencesStore + + @Inject lateinit var appForegroundStateService: AppForegroundStateService + + @AppCoroutineScope + @Inject lateinit var appCoroutineScope: CoroutineScope + private lateinit var coroutineScope: CoroutineScope + + override fun onBind(p0: Intent?): IBinder? = null + + @OptIn(FlowPreview::class) + @SuppressLint("InlinedApi") + override fun onCreate() { + super.onCreate() + Timber.d("LiveLocationSharingService onCreate") + runCatchingExceptions { + bindings().inject(this) + appForegroundStateService.updateIsSharingLiveLocation(true) + coroutineScope = appCoroutineScope.childScope(Dispatchers.Default, "LiveLocationSharingService") + val notificationId = NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.LIVE_LOCATION) + Timber.d("LiveLocationSharingService starting foreground service with notificationId=$notificationId") + ServiceCompat.startForeground( + // service = + this, + // id = + notificationId, + // notification = + notificationCreator.createNotification(), + // foregroundServiceType = + FOREGROUND_SERVICE_TYPE_LOCATION, + ) + startLocationUpdatesListener() + }.onFailure { + Timber.e(it, "Failed to start live location sharing service") + stopSelf() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + private fun startLocationUpdatesListener() { + Timber.d("LiveLocationSharingService listening to location updates") + appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow() + .flatMapLatest { minDistanceMeters -> + val locationProvider = AndroidLocationProvider( + context = applicationContext, + updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds, + minDistanceMeters = minDistanceMeters.toFloat(), + desiredAccuracy = DesiredAccuracy.Balanced, + coroutineScope = coroutineScope + ) + locationProvider.location + } + .filterNotNull() + .map { location -> + ApiLocation( + lat = location.position.latitude, + lon = location.position.longitude, + accuracy = location.accuracy.toFloat(), + ) + } + .onEach(coordinator::dispatch) + .launchIn(coroutineScope) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Timber.d("LiveLocationSharingService onStartCommand startId=$startId") + return START_STICKY + } + + override fun onDestroy() { + Timber.d("LiveLocationSharingService onDestroy") + if (::coroutineScope.isInitialized) { + coroutineScope.cancel() + } + appForegroundStateService.updateIsSharingLiveLocation(false) + super.onDestroy() + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationEvent.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationEvent.kt index d9ebc8b5af..e560ce805f 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationEvent.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationEvent.kt @@ -17,7 +17,8 @@ sealed interface ShareLocationEvent { val isPinned: Boolean, ) : ShareLocationEvent - data object ShowLiveLocationDurationPicker : ShareLocationEvent + data object InitiateLiveLocationShare : ShareLocationEvent + data object AcceptLiveLocationDisclaimer : ShareLocationEvent data class StartLiveLocationShare(val duration: Duration) : ShareLocationEvent data object StartTrackingUserLocation : ShareLocationEvent diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index 10fddf1e50..b56f9c7cb3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -21,17 +21,22 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Composer +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.location.impl.common.LocationConstraintsCheck import io.element.android.features.location.impl.common.MapDefaults +import io.element.android.features.location.impl.common.SendLiveLocationPermissions import io.element.android.features.location.impl.common.actions.LocationActions import io.element.android.features.location.impl.common.checkLocationConstraints import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsState +import io.element.android.features.location.impl.common.sendLiveLocationPermissions import io.element.android.features.location.impl.common.toDialogState -import io.element.android.features.location.impl.share.ShareLocationState.Dialog.Constraints +import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.messages.api.MessageComposerContext +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.dateformatter.api.DurationFormatter @@ -41,6 +46,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.room.CreateTimelineParams import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType +import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.services.analytics.api.AnalyticsService @@ -63,6 +69,8 @@ class ShareLocationPresenter( private val featureFlagService: FeatureFlagService, private val client: MatrixClient, private val durationFormatter: DurationFormatter, + private val liveLocationShareManager: ActiveLiveLocationShareManager, + private val liveLocationStore: LiveLocationStore, ) : Presenter { @AssistedFactory fun interface Factory { @@ -82,15 +90,39 @@ class ShareLocationPresenter( var dialogState: ShareLocationState.Dialog by remember { mutableStateOf(ShareLocationState.Dialog.None) } + val startLiveLocationAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val currentUser by client.userProfile.collectAsState() + val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms -> + perms.sendLiveLocationPermissions() + } val scope = rememberCoroutineScope() fun checkLocationConstraints() { - val locationConstraints = checkLocationConstraints(permissionsState, locationActions) - dialogState = Constraints(locationConstraints.toDialogState()) + // No need to check SendLiveLocationPermissions here + val locationConstraints = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) + dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState()) trackUserPosition = locationConstraints is LocationConstraintsCheck.Success } + suspend fun computeLiveLocationDialogState(): ShareLocationState.Dialog { + val hasAcceptedDisclaimer = liveLocationStore.hasAcceptedLiveLocationDisclaimer() + val constraintsResult = checkLocationConstraints(permissionsState, locationActions, sendLiveLocationPermissions) + return when { + !hasAcceptedDisclaimer -> { + ShareLocationState.Dialog.LiveLocationDisclaimer + } + constraintsResult is LocationConstraintsCheck.Success -> { + val durations = LIVE_LOCATION_DURATIONS.map { + LiveLocationDuration(duration = it, formatted = durationFormatter.format(it)) + } + ShareLocationState.Dialog.LiveLocationDurations(durations.toImmutableList()) + } + else -> { + ShareLocationState.Dialog.Constraints(constraintsResult.toDialogState()) + } + } + } + LaunchedEffect(permissionsState.permissions) { checkLocationConstraints() } fun handleEvent(event: ShareLocationEvent) { @@ -109,20 +141,23 @@ class ShareLocationPresenter( locationActions.openLocationSettings() dialogState = ShareLocationState.Dialog.None } - ShareLocationEvent.ShowLiveLocationDurationPicker -> { - val constraintsResult = checkLocationConstraints(permissionsState, locationActions) - dialogState = if (constraintsResult is LocationConstraintsCheck.Success) { - val durations = LIVE_LOCATION_DURATIONS.map { - LiveLocationDuration(duration = it, formatted = durationFormatter.format(it)) + ShareLocationEvent.InitiateLiveLocationShare -> scope.launch { + dialogState = computeLiveLocationDialogState() + } + ShareLocationEvent.AcceptLiveLocationDisclaimer -> scope.launch { + liveLocationStore.setAcceptedLiveLocationDisclaimer() + .onSuccess { + dialogState = computeLiveLocationDialogState() } - ShareLocationState.Dialog.LiveLocationDurations(durations.toImmutableList()) - } else { - Constraints(constraintsResult.toDialogState()) - } } is ShareLocationEvent.StartLiveLocationShare -> scope.launch { dialogState = ShareLocationState.Dialog.None - // room.startLiveLocationShare(event.duration.inWholeMilliseconds) + startLiveLocationAction.runUpdatingState { + liveLocationShareManager.startShare( + roomId = room.roomId, + duration = event.duration, + ) + } } ShareLocationEvent.RequestPermissions -> { dialogState = ShareLocationState.Dialog.None @@ -138,6 +173,7 @@ class ShareLocationPresenter( hasLocationPermission = permissionsState.isAnyGranted, canShareLiveLocation = isLiveLocationSharingEnabled, appName = appName, + startLiveLocationAction = startLiveLocationAction.value, eventSink = ::handleEvent, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt index 8b1f494f1e..68598cba04 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.impl.share import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList @@ -19,11 +20,13 @@ data class ShareLocationState( val hasLocationPermission: Boolean, val appName: String, val canShareLiveLocation: Boolean, + val startLiveLocationAction: AsyncAction, val eventSink: (ShareLocationEvent) -> Unit, ) { sealed interface Dialog { data object None : Dialog data class Constraints(val state: LocationConstraintsDialogState) : Dialog + data object LiveLocationDisclaimer : Dialog data class LiveLocationDurations(val durations: ImmutableList) : Dialog } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt index facef74346..ae1b765b6b 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.location.impl.share import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.persistentListOf @@ -51,6 +52,18 @@ class ShareLocationStateProvider : PreviewParameterProvider trackUserPosition = true, hasLocationPermission = true, ), + aShareLocationState( + dialogState = ShareLocationState.Dialog.None, + trackUserPosition = true, + hasLocationPermission = true, + canShareLiveLocation = true, + ), + aShareLocationState( + dialogState = ShareLocationState.Dialog.LiveLocationDisclaimer, + trackUserPosition = true, + hasLocationPermission = true, + canShareLiveLocation = true, + ), aShareLocationState( dialogState = ShareLocationState.Dialog.LiveLocationDurations( persistentListOf( @@ -73,6 +86,7 @@ fun aShareLocationState( hasLocationPermission: Boolean = false, canShareLiveLocation: Boolean = false, appName: String = APP_NAME, + startLiveLocationAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (ShareLocationEvent) -> Unit = {}, ): ShareLocationState { return ShareLocationState( @@ -82,6 +96,7 @@ fun aShareLocationState( hasLocationPermission = hasLocationPermission, canShareLiveLocation = canShareLiveLocation, appName = appName, + startLiveLocationAction = startLiveLocationAction, eventSink = eventSink ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 1e163f417d..e20ee3a7a5 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -29,7 +29,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -44,11 +43,16 @@ import io.element.android.features.location.impl.common.ui.LocationFloatingActio import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold import io.element.android.features.location.impl.common.ui.UserLocationPuck import io.element.android.features.location.impl.common.ui.rememberUserLocationState -import io.element.android.libraries.androidutils.system.toast +import io.element.android.features.location.impl.share.ShareLocationEvent.StartLiveLocationShare +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.LocationPin import io.element.android.libraries.designsystem.components.PinVariant +import io.element.android.libraries.designsystem.components.async.AsyncIndicator +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost +import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.components.dialogs.ListDialog import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.RadioButtonListItem @@ -74,7 +78,6 @@ fun ShareLocationView( navigateUp: () -> Unit, modifier: Modifier = Modifier, ) { - val context = LocalContext.current when (val dialogState = state.dialogState) { ShareLocationState.Dialog.None -> Unit is ShareLocationState.Dialog.Constraints -> LocationConstraintsDialog( @@ -85,12 +88,17 @@ fun ShareLocationView( onOpenLocationSettings = { state.eventSink(ShareLocationEvent.OpenLocationSettings) }, onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) }, ) + ShareLocationState.Dialog.LiveLocationDisclaimer -> ConfirmationDialog( + content = stringResource(R.string.screen_share_location_live_location_disclaimer_title), + submitText = stringResource(CommonStrings.action_accept), + cancelText = stringResource(CommonStrings.action_decline), + onSubmitClick = { state.eventSink(ShareLocationEvent.AcceptLiveLocationDisclaimer) }, + onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) }, + ) is ShareLocationState.Dialog.LiveLocationDurations -> LiveLocationDurationDialog( durations = dialogState.durations, onSelectDuration = { duration -> - state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration)) - context.toast("Not implemented yet!") - navigateUp() + state.eventSink(StartLiveLocationShare(duration)) }, onDismiss = { state.eventSink(ShareLocationEvent.DismissDialog) }, ) @@ -160,10 +168,46 @@ fun ShareLocationView( .align(Alignment.TopEnd) .padding(all = 16.dp), ) + StartLiveLocationActionView(state.startLiveLocationAction, navigateUp) } ) } +@Composable +private fun StartLiveLocationActionView( + action: AsyncAction, + onActionSuccess: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier) { + val asyncIndicatorState = rememberAsyncIndicatorState() + AsyncIndicatorHost(state = asyncIndicatorState) + + when (action) { + is AsyncAction.Loading -> { + LaunchedEffect(action) { + asyncIndicatorState.enqueue { + AsyncIndicator.Loading(text = stringResource(CommonStrings.common_waiting_live_location)) + } + } + } + is AsyncAction.Failure -> { + LaunchedEffect(action) { + asyncIndicatorState.enqueue(AsyncIndicator.DURATION_SHORT) { + AsyncIndicator.Failure( + text = stringResource(CommonStrings.common_something_went_wrong), + ) + } + } + } + is AsyncAction.Success -> { + LaunchedEffect(action) { onActionSuccess() } + } + else -> Unit + } + } +} + @Composable private fun BottomSheetContent( cameraState: CameraState, @@ -202,7 +246,7 @@ private fun BottomSheetContent( } if (state.canShareLiveLocation) { ShareLiveLocationItem { - state.eventSink(ShareLocationEvent.ShowLiveLocationDurationPicker) + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) } } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvent.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvent.kt index 6a3e3521e0..34132dccf3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvent.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationEvent.kt @@ -17,4 +17,5 @@ sealed interface ShowLocationEvent { data object RequestPermissions : ShowLocationEvent data object OpenAppSettings : ShowLocationEvent data object OpenLocationSettings : ShowLocationEvent + data object StopLocationSharing : ShowLocationEvent } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 43d3aa6d00..207b4b01dd 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -15,14 +15,17 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.location.impl.common.LocationConstraintsCheck import io.element.android.features.location.impl.common.MapDefaults +import io.element.android.features.location.impl.common.SendLiveLocationPermissions import io.element.android.features.location.impl.common.actions.LocationActions import io.element.android.features.location.impl.common.checkLocationConstraints import io.element.android.features.location.impl.common.permissions.PermissionsEvents @@ -45,6 +48,7 @@ import io.element.android.services.toolbox.api.strings.StringProvider import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch @AssistedInject class ShowLocationPresenter( @@ -55,6 +59,7 @@ class ShowLocationPresenter( private val dateFormatter: DateFormatter, private val stringProvider: StringProvider, private val joinedRoom: JoinedRoom, + private val liveLocationShareManager: ActiveLiveLocationShareManager, ) : Presenter { @AssistedFactory fun interface Factory { @@ -65,6 +70,7 @@ class ShowLocationPresenter( @Composable override fun present(): ShowLocationState { + val coroutineScope = rememberCoroutineScope() val permissionsState: PermissionsState = permissionsPresenter.present() var isTrackMyLocation by remember { mutableStateOf(false) } val appName by remember { derivedStateOf { buildMeta.applicationName } } @@ -85,7 +91,7 @@ class ShowLocationPresenter( } is ShowLocationEvent.TrackMyLocation -> { if (event.enabled) { - val locationConstraints = checkLocationConstraints(permissionsState, locationActions) + val locationConstraints = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) isTrackMyLocation = locationConstraints is LocationConstraintsCheck.Success dialogState = locationConstraints.toDialogState() } else { @@ -102,6 +108,9 @@ class ShowLocationPresenter( dialogState = LocationConstraintsDialogState.None } ShowLocationEvent.RequestPermissions -> permissionsState.eventSink(PermissionsEvents.RequestPermissions) + ShowLocationEvent.StopLocationSharing -> coroutineScope.launch { + liveLocationShareManager.stopShare(joinedRoom.roomId) + } } } @@ -127,6 +136,7 @@ class ShowLocationPresenter( location = mode.location, isLive = false, assetType = mode.assetType, + isOwnUser = mode.senderId == joinedRoom.sessionId ) ) } @@ -163,6 +173,7 @@ class ShowLocationPresenter( location = location, isLive = true, assetType = lastLocation.assetType, + isOwnUser = share.userId == joinedRoom.sessionId ) } .toImmutableList() diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index b6a60f35db..720697cbf7 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -38,7 +38,10 @@ data class LocationShareItem( val location: Location, val isLive: Boolean, val assetType: AssetType?, -) + val isOwnUser: Boolean +) { + val canStopSharing = isLive && isOwnUser +} fun LocationShareItem.toMarkerData(): LocationMarkerData { val pinVariant = if (assetType == AssetType.PIN) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index 2bbf4ae34e..1c7b9a3160 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -81,6 +81,7 @@ fun aLocationShareItem( assetType: AssetType? = null, formattedTimestamp: String = "Shared 1 min ago", location: Location = Location(1.23, 2.34, 4f), + isOwnUser: Boolean = false, ) = LocationShareItem( userId = userId, displayName = displayName, @@ -89,4 +90,5 @@ fun aLocationShareItem( location = location, isLive = isLive, assetType = assetType, + isOwnUser = isOwnUser, ) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 7ac5946723..6766fa6424 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -147,6 +147,7 @@ fun ShowLocationView( LocationShareRow( item = locationShare, onShareClick = { state.eventSink(ShowLocationEvent.Share(locationShare.location)) }, + onStopClick = { state.eventSink(ShowLocationEvent.StopLocationSharing) }, modifier = Modifier.clickable { state.eventSink(ShowLocationEvent.TrackMyLocation(false)) val position = CameraPosition( diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheckTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheckTest.kt index c8e1f21a48..debe95b464 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheckTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/LocationConstraintsCheckTest.kt @@ -21,7 +21,7 @@ class LocationConstraintsCheckTest { ) val locationActions = FakeLocationActions(isLocationEnabled = true) - val result = checkLocationConstraints(permissionsState, locationActions) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) assertThat(result).isEqualTo(LocationConstraintsCheck.Success) } @@ -33,7 +33,7 @@ class LocationConstraintsCheckTest { ) val locationActions = FakeLocationActions(isLocationEnabled = true) - val result = checkLocationConstraints(permissionsState, locationActions) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) assertThat(result).isEqualTo(LocationConstraintsCheck.Success) } @@ -45,7 +45,7 @@ class LocationConstraintsCheckTest { ) val locationActions = FakeLocationActions(isLocationEnabled = false) - val result = checkLocationConstraints(permissionsState, locationActions) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) assertThat(result).isEqualTo(LocationConstraintsCheck.LocationServiceDisabled) } @@ -58,7 +58,7 @@ class LocationConstraintsCheckTest { ) val locationActions = FakeLocationActions(isLocationEnabled = true) - val result = checkLocationConstraints(permissionsState, locationActions) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) assertThat(result).isEqualTo(LocationConstraintsCheck.PermissionRationale) } @@ -71,8 +71,20 @@ class LocationConstraintsCheckTest { ) val locationActions = FakeLocationActions(isLocationEnabled = true) - val result = checkLocationConstraints(permissionsState, locationActions) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) assertThat(result).isEqualTo(LocationConstraintsCheck.PermissionDenied) } + + @Test + fun `checkLocationConstraints returns NotEnoughPowerLevel when send permissions are not granted`() { + val permissionsState = aPermissionsState( + permissions = PermissionsState.Permissions.NoneGranted, + shouldShowRationale = false, + ) + val locationActions = FakeLocationActions(isLocationEnabled = true) + val result = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.DEFAULT) + + assertThat(result).isEqualTo(LocationConstraintsCheck.NotEnoughPowerLevel) + } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManagerTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManagerTest.kt new file mode 100644 index 0000000000..85f0e1c33f --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManagerTest.kt @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.emptyPreferences +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.impl.live.service.LiveLocationSharingCoordinator +import io.element.android.libraries.matrix.api.room.location.BeaconInfoUpdate +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.room.FakeJoinedRoom +import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory +import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver +import io.element.android.services.toolbox.api.systemclock.SystemClock +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.assert +import io.element.android.tests.testutils.lambda.lambdaRecorder +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Instant + +@OptIn(ExperimentalCoroutinesApi::class) +class DefaultActiveLiveLocationShareManagerTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `starting the first share starts the coordinator service after the beacon echo and adds an active share`() = runTest { + val startServiceRecorder = lambdaRecorder { } + val stopServiceRecorder = lambdaRecorder { } + val coordinator = createCoordinator( + startService = startServiceRecorder, + stopService = stopServiceRecorder + ) + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val room = FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ) + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { givenGetRoomResult(A_ROOM_ID, room) }, + coordinator = coordinator, + clock = FakeSystemClock(epochMillisResult = 123L), + ) + advanceUntilIdle() + + val result = async { manager.startShare(A_ROOM_ID, 60.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + + assertThat(result.await().isSuccess).isTrue() + assertThat(manager.sharingRoomIds.value).containsExactly(A_ROOM_ID) + assert(startServiceRecorder).isCalledOnce() + assert(stopServiceRecorder).isNeverCalled() + } + + @Test + fun `stopping the last share stops the coordinator service`() = runTest { + val startServiceRecorder = lambdaRecorder { } + val stopServiceRecorder = lambdaRecorder { } + val coordinator = createCoordinator( + startService = startServiceRecorder, + stopService = stopServiceRecorder + ) + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val room = FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ) + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { givenGetRoomResult(A_ROOM_ID, room) }, + coordinator = coordinator, + ) + advanceUntilIdle() + + val startResult = async { manager.startShare(A_ROOM_ID, 15.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(startResult.await().isSuccess).isTrue() + + val result = manager.stopShare(A_ROOM_ID) + + assertThat(result.isSuccess).isTrue() + assertThat(manager.sharingRoomIds.value).isEmpty() + assert(startServiceRecorder).isCalledOnce() + assert(stopServiceRecorder).isCalledOnce() + } + + @Test + fun `two managers with the same room id keep isolated state per session`() = runTest { + val coordinator = createCoordinator() + val beaconInfoUpdatesOne = MutableSharedFlow(replay = 1) + val beaconInfoUpdatesTwo = MutableSharedFlow(replay = 1) + val managerOne = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdatesOne, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ), + ) + }, + coordinator = coordinator, + ) + val managerTwo = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID_2, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdatesTwo, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ), + ) + }, + coordinator = coordinator, + ) + advanceUntilIdle() + + val startResult = async { managerOne.startShare(A_ROOM_ID, 15.minutes) } + beaconInfoUpdatesOne.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(startResult.await().isSuccess).isTrue() + + assertThat(managerOne.sharingRoomIds.value).containsExactly(A_ROOM_ID) + assertThat(managerTwo.sharingRoomIds.value).isEmpty() + } + + @Test + fun `start share persists room expiry after beacon echo`() = runTest { + val liveLocationStore = createLiveLocationStore() + val coordinator = createCoordinator() + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ), + ) + }, + coordinator = coordinator, + liveLocationStore = liveLocationStore, + clock = FakeSystemClock(epochMillisResult = 123L), + ) + advanceUntilIdle() + + val result = async { manager.startShare(A_ROOM_ID, 15.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + + assertThat(result.await().isSuccess).isTrue() + assertThat(liveLocationStore.getLiveLocationExpiries()).containsKey(A_ROOM_ID) + } + + @Test + fun `stop share removes persisted expiry`() = runTest { + val liveLocationStore = createLiveLocationStore() + val coordinator = createCoordinator() + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ), + ) + }, + coordinator = coordinator, + liveLocationStore = liveLocationStore, + ) + advanceUntilIdle() + + val startResult = async { manager.startShare(A_ROOM_ID, 15.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(startResult.await().isSuccess).isTrue() + + manager.stopShare(A_ROOM_ID) + + assertThat(liveLocationStore.getLiveLocationExpiries()).doesNotContainKey(A_ROOM_ID) + } + + @Test + fun `setup restores unexpired stored share and registers coordinator`() = runTest { + val startServiceRecorder = lambdaRecorder { } + val stopServiceRecorder = lambdaRecorder { } + val liveLocationStore = createLiveLocationStore().apply { + setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(10_000L)) + } + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ).apply { + givenGetRoomResult(A_ROOM_ID, FakeJoinedRoom()) + }, + coordinator = createCoordinator( + startService = startServiceRecorder, + stopService = stopServiceRecorder, + ), + liveLocationStore = liveLocationStore, + clock = FakeSystemClock(epochMillisResult = 1_000L), + ) + + assertThat(manager.sharingRoomIds.value).containsExactly(A_ROOM_ID) + assert(startServiceRecorder).isCalledOnce() + assert(stopServiceRecorder).isNeverCalled() + } + + @Test + fun `setup remotely stops expired stored share and removes it from store`() = runTest { + val stopLiveLocationShareResult = lambdaRecorder> { Result.success(Unit) } + val liveLocationStore = createLiveLocationStore().apply { + setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(1_000L)) + } + createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom(stopLiveLocationShareResult = stopLiveLocationShareResult), + ) + }, + coordinator = createCoordinator(), + liveLocationStore = liveLocationStore, + clock = FakeSystemClock(epochMillisResult = 5_000L), + ) + advanceUntilIdle() + assert(stopLiveLocationShareResult).isCalledOnce() + assertThat(liveLocationStore.getLiveLocationExpiries()).isEmpty() + } + + @Test + fun `stop share closes loaded room and removes persisted expiry when room is not tracked`() = runTest { + val stopLiveLocationShareResult = lambdaRecorder> { Result.success(Unit) } + val room = FakeJoinedRoom(stopLiveLocationShareResult = stopLiveLocationShareResult) + val liveLocationStore = createInMemoryLiveLocationStore() + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ).apply { + givenGetRoomResult(A_ROOM_ID, room) + }, + coordinator = createCoordinator(), + liveLocationStore = liveLocationStore, + ) + liveLocationStore.setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(10_000L)) + + val result = manager.stopShare(A_ROOM_ID) + + assertThat(result.isSuccess).isTrue() + assert(stopLiveLocationShareResult).isCalledOnce() + assertThat(liveLocationStore.getLiveLocationExpiries()).doesNotContainKey(A_ROOM_ID) + room.baseRoom.assertDestroyed() + } + + @Test + fun `share is automatically stopped when timeout elapses`() = runTest { + val liveLocationStore = createInMemoryLiveLocationStore() + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val stopLiveLocationShareResult = lambdaRecorder> { Result.success(Unit) } + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = stopLiveLocationShareResult + ), + ) + }, + coordinator = createCoordinator(), + liveLocationStore = liveLocationStore, + clock = FakeSystemClock(epochMillisResult = 123L), + ) + advanceUntilIdle() + + val startResult = async { manager.startShare(A_ROOM_ID, 1.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(startResult.await().isSuccess).isTrue() + + manager.sharingRoomIds.test { + assertThat(awaitItem()).containsExactly(A_ROOM_ID) + assertThat(awaitItem()).isEmpty() + advanceUntilIdle() + assertThat(liveLocationStore.getLiveLocationExpiries()).doesNotContainKey(A_ROOM_ID) + assert(stopLiveLocationShareResult).isCalledExactly(2) + } + } + + @Test + fun `restored share is automatically stopped when remaining timeout elapses`() = runTest { + val liveLocationStore = createInMemoryLiveLocationStore().apply { + setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(6_000L)) + } + val stopLiveLocationShareLambda = lambdaRecorder> { Result.success(Unit) } + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + stopLiveLocationShareResult = stopLiveLocationShareLambda + ), + ) + }, + coordinator = createCoordinator(), + liveLocationStore = liveLocationStore, + clock = FakeSystemClock(epochMillisResult = 1_000L), + ) + + manager.sharingRoomIds.test { + assertThat(awaitItem()).containsExactly(A_ROOM_ID) + assertThat(awaitItem()).isEmpty() + advanceUntilIdle() + assertThat(liveLocationStore.getLiveLocationExpiries()).doesNotContainKey(A_ROOM_ID) + assert(stopLiveLocationShareLambda).isCalledOnce() + } + } + + @Test + fun `session deleted clears local state`() = runTest { + val startServiceRecorder = lambdaRecorder { } + val stopServiceRecorder = lambdaRecorder { } + val liveLocationStore = createInMemoryLiveLocationStore() + val sessionObserver = FakeSessionObserver() + val beaconInfoUpdates = MutableSharedFlow(replay = 1) + val manager = createManager( + client = FakeMatrixClient( + sessionId = A_SESSION_ID, + sessionCoroutineScope = backgroundScope, + ownBeaconInfoUpdates = beaconInfoUpdates, + ).apply { + givenGetRoomResult( + A_ROOM_ID, + FakeJoinedRoom( + startLiveLocationShareResult = { Result.success(AN_EVENT_ID) }, + stopLiveLocationShareResult = { Result.success(Unit) }, + ), + ) + }, + coordinator = createCoordinator( + startService = startServiceRecorder, + stopService = stopServiceRecorder, + ), + liveLocationStore = liveLocationStore, + sessionObserver = sessionObserver, + ) + advanceUntilIdle() + + val firstStart = async { manager.startShare(A_ROOM_ID, 15.minutes) } + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(firstStart.await().isSuccess).isTrue() + + sessionObserver.onSessionDeleted(A_SESSION_ID.value) + advanceUntilIdle() + + assertThat(manager.sharingRoomIds.value).isEmpty() + assertThat(liveLocationStore.getLiveLocationExpiries()).doesNotContainKey(A_ROOM_ID) + assert(startServiceRecorder).isCalledOnce() + assert(stopServiceRecorder).isCalledOnce() + + val secondStart = async { manager.startShare(A_ROOM_ID, 15.minutes) } + advanceUntilIdle() + assertThat(secondStart.isCompleted).isFalse() + + beaconInfoUpdates.emit(BeaconInfoUpdate(roomId = A_ROOM_ID, beaconId = AN_EVENT_ID, isLive = true)) + assertThat(secondStart.await().isSuccess).isTrue() + } + + private suspend fun createManager( + client: FakeMatrixClient = FakeMatrixClient(sessionId = A_SESSION_ID), + coordinator: LiveLocationSharingCoordinator = createCoordinator(), + liveLocationStore: LiveLocationStore = createLiveLocationStore(), + clock: SystemClock = FakeSystemClock(), + sessionObserver: SessionObserver = FakeSessionObserver(), + ): DefaultActiveLiveLocationShareManager { + return DefaultActiveLiveLocationShareManager( + matrixClient = client, + coordinator = coordinator, + liveLocationStore = liveLocationStore, + clock = clock, + sessionObserver = sessionObserver, + ).apply { + setup() + } + } + + private fun createCoordinator( + startService: () -> Unit = {}, + stopService: () -> Unit = {}, + nowMillis: () -> Long = { 0L }, + ): LiveLocationSharingCoordinator { + return LiveLocationSharingCoordinator( + startService = startService, + stopService = stopService, + nowMillis = nowMillis, + ) + } + + private fun createLiveLocationStore( + sessionId: io.element.android.libraries.matrix.api.core.SessionId = A_SESSION_ID, + preferenceDataStoreFactory: PreferenceDataStoreFactory = FakePreferenceDataStoreFactory(), + ): LiveLocationStore { + return LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = sessionId, + ) + } + + private fun createInMemoryLiveLocationStore( + sessionId: io.element.android.libraries.matrix.api.core.SessionId = A_SESSION_ID, + ): LiveLocationStore { + val preferenceDataStoreFactory = object : PreferenceDataStoreFactory { + override fun create(name: String): DataStore { + var preferences: Preferences = emptyPreferences() + return object : DataStore { + override val data: Flow + get() = flowOf(preferences) + + override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences): Preferences { + preferences = transform(preferences) + return preferences + } + } + } + } + return createLiveLocationStore( + sessionId = sessionId, + preferenceDataStoreFactory = preferenceDataStoreFactory, + ) + } +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt new file mode 100644 index 0000000000..f74322b4c5 --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.live + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.api.Location +import io.element.android.features.location.impl.live.service.LiveLocationReceiver +import io.element.android.features.location.impl.live.service.LiveLocationSharingCoordinator +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID_2 +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class LiveLocationSharingCoordinatorTest { + @Test + fun `first registration starts the service and last unregister stops it`() = runTest { + var startCount = 0 + var stopCount = 0 + val coordinator = LiveLocationSharingCoordinator( + startService = { startCount++ }, + stopService = { stopCount++ }, + nowMillis = { 0L }, + ) + + coordinator.register(A_SESSION_ID, LiveLocationReceiver { }) + coordinator.unregister(A_SESSION_ID) + + assertThat(startCount).isEqualTo(1) + assertThat(stopCount).isEqualTo(1) + } + + @Test + fun `dispatch isolates receiver failures and still reaches later receivers`() = runTest { + val delivered = mutableListOf() + val coordinator = LiveLocationSharingCoordinator( + startService = { }, + stopService = { }, + nowMillis = { 4_000L }, + ) + + coordinator.register(A_SESSION_ID) { error("boom") } + coordinator.register(A_SESSION_ID_2) { location -> delivered += location } + coordinator.dispatch(Location(lat = 1.0, lon = 2.0, accuracy = 3f)) + + assertThat(delivered).containsExactly(Location(lat = 1.0, lon = 2.0, accuracy = 3f)) + } + + @Test + fun `dispatch delivers first location immediately`() = runTest { + var nowMillis = 4_000L + val delivered = mutableListOf() + val coordinator = LiveLocationSharingCoordinator( + startService = { }, + stopService = { }, + nowMillis = { nowMillis }, + ) + + coordinator.register(A_SESSION_ID) { location -> delivered += location } + + val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) + + coordinator.dispatch(firstLocation) + + assertThat(delivered).containsExactly(firstLocation) + } + + @Test + fun `dispatch drops updates inside the throttle window`() = runTest { + var nowMillis = 4_000L + val delivered = mutableListOf() + val coordinator = LiveLocationSharingCoordinator( + startService = { }, + stopService = { }, + nowMillis = { nowMillis }, + ) + + coordinator.register(A_SESSION_ID) { location -> delivered += location } + + val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) + val secondLocation = Location(lat = 4.0, lon = 5.0, accuracy = 6f) + + coordinator.dispatch(firstLocation) + nowMillis += 500 + coordinator.dispatch(secondLocation) + + assertThat(delivered).containsExactly(firstLocation) + } + + @Test + fun `dispatch delivers next update after the throttle window elapses`() = runTest { + var nowMillis = 4_000L + val delivered = mutableListOf() + val coordinator = LiveLocationSharingCoordinator( + startService = { }, + stopService = { }, + nowMillis = { nowMillis }, + ) + + coordinator.register(A_SESSION_ID) { location -> delivered += location } + + val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) + val secondLocation = Location(lat = 4.0, lon = 5.0, accuracy = 6f) + + coordinator.dispatch(firstLocation) + nowMillis += 3_000 + coordinator.dispatch(secondLocation) + + assertThat(delivered).containsExactly(firstLocation, secondLocation).inOrder() + } +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt index edd000e02c..9b1a14fb51 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt @@ -13,6 +13,8 @@ import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter +import io.element.android.features.location.impl.live.LiveLocationStore +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.dateformatter.test.FakeDurationFormatter import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -20,8 +22,10 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom +import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.node.TestParentNode +import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -30,16 +34,17 @@ class DefaultShareLocationEntryPointTest { val instantTaskExecutorRule = InstantTaskExecutorRule() @Test - fun `test node builder`() { + fun `test node builder`() = runTest { val entryPoint = DefaultShareLocationEntryPoint() val parentNode = TestParentNode.create { buildContext, plugins -> + val room = FakeJoinedRoom() ShareLocationNode( buildContext = buildContext, plugins = plugins, presenterFactory = { timelineMode: Timeline.Mode -> ShareLocationPresenter( permissionsPresenterFactory = { FakePermissionsPresenter() }, - room = FakeJoinedRoom(), + room = room, timelineMode = timelineMode, analyticsService = FakeAnalyticsService(), messageComposerContext = FakeMessageComposerContext(), @@ -48,6 +53,11 @@ class DefaultShareLocationEntryPointTest { featureFlagService = FakeFeatureFlagService(), client = FakeMatrixClient(), durationFormatter = FakeDurationFormatter(), + liveLocationShareManager = FakeActiveLiveLocationShareManager(), + liveLocationStore = LiveLocationStore( + preferenceDataStoreFactory = FakePreferenceDataStoreFactory(), + sessionId = room.sessionId, + ), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index 46bc4a55df..855deba694 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -10,6 +10,9 @@ package io.element.android.features.location.impl.share +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.emptyPreferences import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test @@ -22,28 +25,46 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.impl.live.LiveLocationStore +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.dateformatter.test.FakeDurationFormatter 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.RoomId +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom +import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.matrix.test.timeline.FakeTimeline +import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory +import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.assert 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.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import kotlin.time.Duration +import kotlin.time.Duration.Companion.hours class ShareLocationPresenterTest { @get:Rule @@ -59,9 +80,11 @@ class ShareLocationPresenterTest { private val durationFormatter = FakeDurationFormatter() - private fun createShareLocationPresenter( + private fun TestScope.createShareLocationPresenter( joinedRoom: JoinedRoom = FakeJoinedRoom(), locationActions: FakeLocationActions = fakeLocationActions, + liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), + liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), ): ShareLocationPresenter = ShareLocationPresenter( permissionsPresenterFactory = { fakePermissionsPresenter }, room = joinedRoom, @@ -73,6 +96,8 @@ class ShareLocationPresenterTest { featureFlagService = fakeFeatureFlagService, client = fakeMatrixClient, durationFormatter = durationFormatter, + liveLocationShareManager = liveLocationShareManager, + liveLocationStore = liveLocationStore, ) @Test @@ -296,7 +321,15 @@ class ShareLocationPresenterTest { @Test fun `ShowLiveLocationDurationPicker shows duration dialog when constraints pass`() = runTest { - val shareLocationPresenter = createShareLocationPresenter() + val joinedRoom = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + roomPermissions = grantedSendLiveLocationPermissions() + ) + ) + val locationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId).apply { + setAcceptedLiveLocationDisclaimer().getOrThrow() + } + val shareLocationPresenter = createShareLocationPresenter(joinedRoom = joinedRoom, liveLocationStore = locationStore) fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.AllGranted, @@ -307,7 +340,7 @@ class ShareLocationPresenterTest { shareLocationPresenter.test { skipItems(1) val initialState = awaitItem() - initialState.eventSink(ShareLocationEvent.ShowLiveLocationDurationPicker) + initialState.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val durationDialogState = awaitItem() assertThat(durationDialogState.dialogState).isInstanceOf(ShareLocationState.Dialog.LiveLocationDurations::class.java) @@ -315,9 +348,155 @@ class ShareLocationPresenterTest { } } + @Test + fun `ShowLiveLocationDurationPicker shows disclaimer when acceptance is missing`() = runTest { + val presenter = createShareLocationPresenter() + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + presenter.test { + skipItems(1) + val state = awaitItem() + + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) + val dialogState = awaitItem() + + assertThat(dialogState.dialogState).isEqualTo(ShareLocationState.Dialog.LiveLocationDisclaimer) + } + } + + @Test + fun `AcceptLiveLocationDisclaimer persists acceptance and shows durations`() = runTest { + val joinedRoom = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + roomPermissions = grantedSendLiveLocationPermissions() + ) + ) + val locationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId) + val presenter = createShareLocationPresenter(joinedRoom = joinedRoom, liveLocationStore = locationStore) + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + presenter.test { + skipItems(1) + val state = awaitItem() + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) + awaitItem() + + state.eventSink(ShareLocationEvent.AcceptLiveLocationDisclaimer) + val durationState = awaitItem() + + assertThat(locationStore.hasAcceptedLiveLocationDisclaimer()).isTrue() + assertThat(durationState.dialogState).isInstanceOf(ShareLocationState.Dialog.LiveLocationDurations::class.java) + } + } + + @Test + fun `AcceptLiveLocationDisclaimer keeps disclaimer gate active when persistence fails`() = runTest { + val joinedRoom = FakeJoinedRoom() + val presenter = createShareLocationPresenter( + joinedRoom = joinedRoom, + liveLocationStore = createFailingLiveLocationStore(sessionId = joinedRoom.sessionId), + ) + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + presenter.test { + skipItems(1) + val state = awaitItem() + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) + val disclaimerState = awaitItem() + + disclaimerState.eventSink(ShareLocationEvent.AcceptLiveLocationDisclaimer) + advanceUntilIdle() + + expectNoEvents() + } + } + + @Test + fun `ShowLiveLocationDurationPicker bypasses disclaimer when already accepted`() = runTest { + val joinedRoom = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + roomPermissions = grantedSendLiveLocationPermissions() + ) + ) + val locationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId).apply { + setAcceptedLiveLocationDisclaimer().getOrThrow() + } + val presenter = createShareLocationPresenter(joinedRoom = joinedRoom, liveLocationStore = locationStore) + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + presenter.test { + skipItems(1) + val state = awaitItem() + + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) + val durationState = awaitItem() + + assertThat(durationState.dialogState).isInstanceOf(ShareLocationState.Dialog.LiveLocationDurations::class.java) + } + } + + @Test + fun `ShowLiveLocationDurationPicker uses the active session disclaimer state`() = runTest { + val joinedRoom = FakeJoinedRoom(baseRoom = FakeBaseRoom(sessionId = SessionId("@alice:server"))) + createLiveLocationStore(sessionId = SessionId("@bob:server")) + .setAcceptedLiveLocationDisclaimer() + .getOrThrow() + val presenter = createShareLocationPresenter( + joinedRoom = joinedRoom, + liveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), + ) + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + presenter.test { + skipItems(1) + val state = awaitItem() + + state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) + val dialogState = awaitItem() + + assertThat(dialogState.dialogState).isEqualTo(ShareLocationState.Dialog.LiveLocationDisclaimer) + } + } + @Test fun `ShowLiveLocationDurationPicker shows constraint dialog when permissions denied`() = runTest { - val shareLocationPresenter = createShareLocationPresenter() + val joinedRoom = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + roomPermissions = grantedSendLiveLocationPermissions() + ) + ) + val locationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId).apply { + setAcceptedLiveLocationDisclaimer().getOrThrow() + } + val shareLocationPresenter = createShareLocationPresenter( + joinedRoom = joinedRoom, + liveLocationStore = locationStore, + ) fakePermissionsPresenter.givenState( aPermissionsState( permissions = PermissionsState.Permissions.NoneGranted, @@ -332,7 +511,7 @@ class ShareLocationPresenterTest { initialState.eventSink(ShareLocationEvent.DismissDialog) val dismissedState = awaitItem() - dismissedState.eventSink(ShareLocationEvent.ShowLiveLocationDurationPicker) + dismissedState.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val constraintDialogState = awaitItem() assertThat(constraintDialogState.dialogState).isEqualTo( @@ -447,4 +626,62 @@ class ShareLocationPresenterTest { cancelAndIgnoreRemainingEvents() } } + + @Test + fun `StartLiveLocationShare event calls manager startShare`() = runTest { + val startShareLambda = lambdaRecorder { _: RoomId, _: Duration -> Result.success(Unit) } + val manager = FakeActiveLiveLocationShareManager( + startShareLambda = startShareLambda, + ) + val shareLocationPresenter = createShareLocationPresenter(liveLocationShareManager = manager) + fakePermissionsPresenter.givenState( + aPermissionsState( + permissions = PermissionsState.Permissions.AllGranted, + shouldShowRationale = false, + ) + ) + + shareLocationPresenter.test { + skipItems(1) + val state = awaitItem() + state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration = 1.hours)) + advanceUntilIdle() + assert(startShareLambda).isCalledOnce().with( + value(A_ROOM_ID), + value(1.hours) + ) + cancelAndIgnoreRemainingEvents() + } + } } + +private fun createLiveLocationStore( + sessionId: SessionId = A_SESSION_ID, + preferenceDataStoreFactory: PreferenceDataStoreFactory = FakePreferenceDataStoreFactory(), +): LiveLocationStore { + return LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = sessionId, + ) +} + +private fun createFailingLiveLocationStore(sessionId: SessionId = A_SESSION_ID): LiveLocationStore { + val failingPreferenceDataStoreFactory = object : PreferenceDataStoreFactory { + override fun create(name: String): DataStore = object : DataStore { + override val data: Flow = flowOf(emptyPreferences()) + + override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences): Preferences { + error("Failed to update preferences") + } + } + } + return createLiveLocationStore( + sessionId = sessionId, + preferenceDataStoreFactory = failingPreferenceDataStoreFactory, + ) +} + +private fun grantedSendLiveLocationPermissions(): FakeRoomPermissions = FakeRoomPermissions( + canSendState = { it is StateEventType.BeaconInfo }, + canSendMessage = { it is MessageEventType.Beacon } +) diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt index 63c19ba913..370ccac8ab 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt @@ -143,6 +143,38 @@ class ShareLocationViewTest { clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog) } + + @Test + fun `when disclaimer is displayed user can accept`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setShareLocationView( + aShareLocationState( + dialogState = ShareLocationState.Dialog.LiveLocationDisclaimer, + eventSink = eventsRecorder, + canShareLiveLocation = true, + ), + navigateUp = EnsureNeverCalled(), + ) + + clickOn(CommonStrings.action_accept) + eventsRecorder.assertSingle(ShareLocationEvent.AcceptLiveLocationDisclaimer) + } + + @Test + fun `when disclaimer is displayed user can decline`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setShareLocationView( + aShareLocationState( + dialogState = ShareLocationState.Dialog.LiveLocationDisclaimer, + eventSink = eventsRecorder, + canShareLiveLocation = true, + ), + navigateUp = EnsureNeverCalled(), + ) + + clickOn(CommonStrings.action_decline) + eventsRecorder.assertSingle(ShareLocationEvent.DismissDialog) + } } private fun AndroidComposeUiTest.setShareLocationView( diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index 91df447e2a..985dcc1f9c 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -16,6 +16,7 @@ import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -34,6 +35,7 @@ class DefaultShowLocationEntryPointTest { fun `test node builder`() { val entryPoint = DefaultShowLocationEntryPoint() val parentNode = TestParentNode.create { buildContext, plugins -> + val joinedRoom = FakeJoinedRoom() ShowLocationNode( buildContext = buildContext, plugins = plugins, @@ -45,7 +47,8 @@ class DefaultShowLocationEntryPointTest { buildMeta = aBuildMeta(), dateFormatter = FakeDateFormatter(), stringProvider = FakeStringProvider(), - joinedRoom = FakeJoinedRoom(), + joinedRoom = joinedRoom, + liveLocationShareManager = FakeActiveLiveLocationShareManager(), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/LiveLocationShareComparatorTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/LiveLocationShareComparatorTest.kt index 4042cb4c0c..0b8e04abf8 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/LiveLocationShareComparatorTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/LiveLocationShareComparatorTest.kt @@ -9,7 +9,7 @@ package io.element.android.features.location.impl.show import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare import org.junit.Test class LiveLocationShareComparatorTest { @@ -55,15 +55,3 @@ class LiveLocationShareComparatorTest { assertThat(sortedShares).containsExactly(newerShare, olderShare).inOrder() } } - -private fun aLiveLocationShare( - userId: UserId, - startTimestamp: Long, -): LiveLocationShare { - return LiveLocationShare( - userId = userId, - lastLocation = null, - startTimestamp = startTimestamp, - endTimestamp = startTimestamp + 1_000L, - ) -} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index f38e8dae60..c1f9f6487e 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -20,14 +20,15 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.matrix.api.room.location.LastLocation import io.element.android.libraries.matrix.api.room.location.LiveLocationShare import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom +import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test @@ -60,6 +61,7 @@ class ShowLocationPresenterTest { ), locationActions: FakeLocationActions = fakeLocationActions, joinedRoom: JoinedRoom = FakeJoinedRoom(), + liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), ) = ShowLocationPresenter( mode = mode, permissionsPresenterFactory = { fakePermissionsPresenter }, @@ -68,6 +70,7 @@ class ShowLocationPresenterTest { dateFormatter = fakeDateFormatter, stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, + liveLocationShareManager = liveLocationShareManager, ) @Test @@ -205,7 +208,7 @@ class ShowLocationPresenterTest { ) ) val presenter = createShowLocationPresenter() - presenter.test { + presenter.test { // Skip initial state val initialState = awaitItem() @@ -464,23 +467,3 @@ class ShowLocationPresenterTest { } } } - -private fun aLiveLocationShare( - userId: UserId, - geoUri: String = "geo:48.8584,2.2945", - timestamp: Long = 0L, - startTimestamp: Long = 0L, - endTimestamp: Long = Long.MAX_VALUE, - assetType: AssetType = AssetType.SENDER, -): LiveLocationShare { - return LiveLocationShare( - userId = userId, - lastLocation = LastLocation( - geoUri = geoUri, - timestamp = timestamp, - assetType = assetType, - ), - startTimestamp = startTimestamp, - endTimestamp = endTimestamp, - ) -} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/store/LiveLocationStoreTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/store/LiveLocationStoreTest.kt new file mode 100644 index 0000000000..c42469e705 --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/store/LiveLocationStoreTest.kt @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.store + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.mutablePreferencesOf +import androidx.datastore.preferences.core.stringPreferencesKey +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.impl.live.LiveLocationStore +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory +import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Test +import kotlin.time.Instant + +class LiveLocationStoreTest { + private val preferenceDataStoreFactory = FakePreferenceDataStoreFactory() + + @Test + fun `disclaimer defaults to false`() = runTest { + val store = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = A_SESSION_ID, + ) + + assertThat(store.hasAcceptedLiveLocationDisclaimer()).isFalse() + } + + @Test + fun `disclaimer acceptance is isolated per session`() = runTest { + val firstStore = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = A_SESSION_ID, + ) + val secondStore = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = SessionId("@other:server"), + ) + + firstStore.setAcceptedLiveLocationDisclaimer().getOrThrow() + + assertThat(firstStore.hasAcceptedLiveLocationDisclaimer()).isTrue() + assertThat(secondStore.hasAcceptedLiveLocationDisclaimer()).isFalse() + } + + @Test + fun `can persist and read expiry per room`() = runTest { + val store = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = A_SESSION_ID, + ) + + store.setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(1_000L)).getOrThrow() + + assertThat(store.getLiveLocationExpiries()) + .containsExactly(A_ROOM_ID, Instant.fromEpochMilliseconds(1_000L)) + } + + @Test + fun `removing one expiry leaves others untouched`() = runTest { + val otherRoomId = RoomId("!other:server") + val store = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = A_SESSION_ID, + ) + + store.setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(1_000L)).getOrThrow() + store.setLiveLocationExpiry(otherRoomId, Instant.fromEpochMilliseconds(2_000L)).getOrThrow() + store.removeLiveLocationExpiry(A_ROOM_ID).getOrThrow() + + assertThat(store.getLiveLocationExpiries()) + .containsExactly(otherRoomId, Instant.fromEpochMilliseconds(2_000L)) + } + + @Test + fun `setting expiry twice replaces the existing room value`() = runTest { + val store = LiveLocationStore( + preferenceDataStoreFactory = preferenceDataStoreFactory, + sessionId = A_SESSION_ID, + ) + + store.setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(1_000L)).getOrThrow() + store.setLiveLocationExpiry(A_ROOM_ID, Instant.fromEpochMilliseconds(2_000L)).getOrThrow() + + assertThat(store.getLiveLocationExpiries()) + .containsExactly(A_ROOM_ID, Instant.fromEpochMilliseconds(2_000L)) + } + + @Test + fun `malformed expiry payload returns empty map`() = runTest { + val store = LiveLocationStore( + preferenceDataStoreFactory = createMalformedExpiryPreferenceDataStoreFactory(), + sessionId = A_SESSION_ID, + ) + + assertThat(store.getLiveLocationExpiries()).isEmpty() + } + + private fun createMalformedExpiryPreferenceDataStoreFactory(): PreferenceDataStoreFactory { + return object : PreferenceDataStoreFactory { + override fun create(name: String): DataStore { + var preferences: Preferences = mutablePreferencesOf( + stringPreferencesKey("live_location_expiries") to "not valid" + ) + return object : DataStore { + override val data: Flow + get() = flowOf(preferences) + + override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences): Preferences { + preferences = transform(preferences) + return preferences + } + } + } + } + } +} diff --git a/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeActiveLiveLocationShareManager.kt b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeActiveLiveLocationShareManager.kt new file mode 100644 index 0000000000..255c181ac1 --- /dev/null +++ b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeActiveLiveLocationShareManager.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.test + +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlin.time.Duration + +class FakeActiveLiveLocationShareManager( + val setupLambda: () -> Unit = { lambdaError() }, + val startShareLambda: (roomId: RoomId, duration: Duration) -> Result = { _, _ -> lambdaError() }, + val stopShareLambda: (roomId: RoomId) -> Result = { _ -> lambdaError() }, +) : ActiveLiveLocationShareManager { + private val _sharingRoomIds = MutableStateFlow(emptySet()) + override val sharingRoomIds: StateFlow> = _sharingRoomIds + + override suspend fun setup() { + setupLambda() + } + + override suspend fun startShare(roomId: RoomId, duration: Duration): Result = simulateLongTask { + startShareLambda(roomId, duration).onSuccess { + _sharingRoomIds.update { + it + roomId + } + } + } + + override suspend fun stopShare(roomId: RoomId): Result = simulateLongTask { + stopShareLambda(roomId).onSuccess { + _sharingRoomIds.update { + it - roomId + } + } + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt index bef8ca84d6..4d621e417f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt @@ -18,6 +18,8 @@ sealed interface MessagesEvent { data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : MessagesEvent data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvent data class OnUserClicked(val user: MatrixUser) : MessagesEvent + data object StopLiveLocationShare : MessagesEvent + data object ShowLiveLocationShare : MessagesEvent data object MarkAsFullyReadAndExit : MessagesEvent } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index ff46ce0041..d20dc4b38e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -278,6 +278,10 @@ class MessagesFlowNode( backstack.push(NavTarget.EditPoll(Timeline.Mode.Live, eventId)) } + override fun navigateToCurrentLiveLocation() { + backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + } + override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { val callData = CallData( sessionId = sessionId, @@ -513,6 +517,10 @@ class MessagesFlowNode( backstack.push(NavTarget.EditPoll(Timeline.Mode.Thread(navTarget.threadRootId), eventId)) } + override fun navigateToCurrentLiveLocation() { + backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + } + override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { val callData = CallData( sessionId = sessionId, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index e475f579c3..6113b68aab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -26,5 +26,6 @@ interface MessagesNavigator { fun navigateToMember(userId: UserId) fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) fun navigateToDeveloperSettings() + fun navigateToCurrentLiveLocation() fun close() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 308cda506e..a9ce2f5ba1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -127,6 +127,7 @@ class MessagesNode( fun navigateToSendLocation() fun navigateToCreatePoll() fun navigateToEditPoll(eventId: EventId) + fun navigateToCurrentLiveLocation() fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) fun navigateToRoomDetails() @@ -239,6 +240,10 @@ class MessagesNode( callback.navigateToDeveloperSettings() } + override fun navigateToCurrentLiveLocation() { + callback.navigateToCurrentLiveLocation() + } + private fun displaySameRoomToast() { context.toast(CommonStrings.screen_room_permalink_same_room_android) } 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 5271906fff..1d6f1d3c1b 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 @@ -27,6 +27,8 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.PinUnpinAction import io.element.android.appconfig.MessageComposerConfig +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager +import io.element.android.features.location.api.live.isCurrentlySharing import io.element.android.features.messages.api.timeline.HtmlConverterProvider import io.element.android.features.messages.impl.MessagesState.Threads import io.element.android.features.messages.impl.actionlist.ActionListState @@ -79,6 +81,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState +import io.element.android.libraries.matrix.api.timeline.Timeline 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 @@ -126,6 +129,7 @@ class MessagesPresenter( private val featureFlagService: FeatureFlagService, private val addRecentEmoji: AddRecentEmoji, private val markAsFullyRead: MarkAsFullyRead, + private val liveLocationShareManager: ActiveLiveLocationShareManager, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, ) : Presenter { @AssistedFactory @@ -172,6 +176,7 @@ class MessagesPresenter( } val canOpenThreadList by featureFlagService.isFeatureEnabledFlow(FeatureFlags.RoomThreadList).collectAsState(initial = false) + val isCurrentlySharingLiveLocationInRoom by remember { liveLocationShareManager.isCurrentlySharing(room.roomId) }.collectAsState() val userEventPermissions by room.permissionsAsState(UserEventPermissions.DEFAULT) { perms -> perms.userEventPermissions() @@ -260,6 +265,18 @@ class MessagesPresenter( is MessagesEvent.OnUserClicked -> { roomMemberModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.user)) } + MessagesEvent.StopLiveLocationShare -> { + localCoroutineScope.launch { + liveLocationShareManager.stopShare(room.roomId) + .onFailure { + Timber.e(it, "Failed to stop live location share for roomId=${room.roomId}") + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_error)) + } + } + } + MessagesEvent.ShowLiveLocationShare -> { + navigator.navigateToCurrentLiveLocation() + } is MessagesEvent.MarkAsFullyReadAndExit -> if (!markingAsReadAndExiting.getAndSet(true)) { coroutineScope.launch { val latestEventId = room.liveTimeline.getLatestEventId().getOrElse { @@ -311,6 +328,7 @@ class MessagesPresenter( // TODO calculate this properly based on the thread list and the read state of each thread hasUnreadThreads = false, ), + showLiveLocationShareBanner = isCurrentlySharingLiveLocationInRoom && timelineState.timelineMode !is Timeline.Mode.Thread, eventSink = ::handleEvent, ) } 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 862f30832b..a16485c6f7 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 @@ -58,6 +58,7 @@ data class MessagesState( val topBarSharedHistoryIcon: SharedHistoryIcon, val successorRoom: SuccessorRoom?, val threads: Threads, + val showLiveLocationShareBanner: Boolean, val eventSink: (MessagesEvent) -> Unit ) { val isTombstoned = successorRoom != null 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 14c83db833..6389089e07 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 @@ -80,6 +80,7 @@ open class MessagesStateProvider : PreviewParameterProvider { currentPinnedMessageIndex = 0, ), ), + aMessagesState(isCurrentlySharingLiveLocationInRoom = true), aMessagesState(successorRoom = SuccessorRoom(RoomId("!id:domain"), null)), aMessagesState( timelineState = aTimelineState( @@ -127,6 +128,7 @@ fun aMessagesState( hasThreads = false, hasUnreadThreads = false, ), + isCurrentlySharingLiveLocationInRoom: Boolean = false, eventSink: (MessagesEvent) -> Unit = {}, ) = MessagesState( roomId = RoomId("!id:domain"), @@ -156,6 +158,7 @@ fun aMessagesState( topBarSharedHistoryIcon = topBarSharedHistoryIcon, successorRoom = successorRoom, threads = threads, + showLiveLocationShareBanner = isCurrentlySharingLiveLocationInRoom, 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 5a0b14b820..3e6f14e805 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 @@ -56,6 +56,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.location.api.LiveLocationSharingBanner import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvent import io.element.android.features.messages.impl.actionlist.ActionListEvent import io.element.android.features.messages.impl.actionlist.ActionListView @@ -205,15 +206,15 @@ fun MessagesView( val expandableState = rememberExpandableBottomSheetLayoutState() ExpandableBottomSheetLayout( modifier = modifier - .fillMaxSize() - .imePadding() - .systemBarsPadding() - .onSizeChanged { size -> - // Let the composer takes at max half of the available height. - // The value will be different if the soft keyboard is displayed - // or not. - maxComposerHeightPx = (size.height * 0.5f).toInt() - }, + .fillMaxSize() + .imePadding() + .systemBarsPadding() + .onSizeChanged { size -> + // Let the composer takes at max half of the available height. + // The value will be different if the soft keyboard is displayed + // or not. + maxComposerHeightPx = (size.height * 0.5f).toInt() + }, content = { Scaffold( contentWindowInsets = WindowInsets.statusBars, @@ -250,8 +251,8 @@ fun MessagesView( content = { padding -> Box( modifier = Modifier - .padding(padding) - .consumeWindowInsets(padding) + .padding(padding) + .consumeWindowInsets(padding) ) { MessagesViewContent( state = state, @@ -288,10 +289,10 @@ fun MessagesView( SuggestionsPickerView( modifier = Modifier - .shadow(10.dp) - .background(ElementTheme.colors.bgCanvasDefault) - .align(Alignment.BottomStart) - .heightIn(max = 230.dp), + .shadow(10.dp) + .background(ElementTheme.colors.bgCanvasDefault) + .align(Alignment.BottomStart) + .heightIn(max = 230.dp), roomId = state.roomId, roomName = state.roomName, roomAvatarData = state.roomAvatar, @@ -467,9 +468,9 @@ private fun MessagesViewContent( ) { Box( modifier = modifier - .fillMaxSize() - .navigationBarsPadding() - .imePadding(), + .fillMaxSize() + .navigationBarsPadding() + .imePadding(), ) { AttachmentsBottomSheet( state = state.composerState, @@ -520,25 +521,34 @@ private fun MessagesViewContent( ) if (state.timelineState.timelineMode !is Timeline.Mode.Thread) { - AnimatedVisibility( - visible = state.pinnedMessagesBannerState is PinnedMessagesBannerState.Visible && scrollBehavior.isVisible, - modifier = Modifier.onSizeChanged { pinnedBannerHeightDp = with(density) { it.height.toDp() } }, - enter = expandVertically(), - exit = shrinkVertically(), - ) { - fun focusOnPinnedEvent(eventId: EventId) { - state.timelineState.eventSink( - TimelineEvent.FocusOnEvent(eventId = eventId, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds) + Column { + AnimatedVisibility( + visible = state.pinnedMessagesBannerState is PinnedMessagesBannerState.Visible && scrollBehavior.isVisible, + modifier = Modifier.onSizeChanged { pinnedBannerHeightDp = with(density) { it.height.toDp() } }, + enter = expandVertically(), + exit = shrinkVertically(), + ) { + fun focusOnPinnedEvent(eventId: EventId) { + state.timelineState.eventSink( + TimelineEvent.FocusOnEvent(eventId = eventId, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds) + ) + } + PinnedMessagesBannerView( + state = state.pinnedMessagesBannerState, + onClick = ::focusOnPinnedEvent, + onViewAllClick = onViewAllPinnedMessagesClick, + ) + } + if (state.showLiveLocationShareBanner) { + LiveLocationSharingBanner( + onClick = { state.eventSink(MessagesEvent.ShowLiveLocationShare) }, + onStopClick = { state.eventSink(MessagesEvent.StopLiveLocationShare) } ) } - PinnedMessagesBannerView( - state = state.pinnedMessagesBannerState, - onClick = ::focusOnPinnedEvent, - onViewAllClick = onViewAllPinnedMessagesClick, - ) } - knockRequestsBannerView() } + + knockRequestsBannerView() } } } @@ -587,9 +597,9 @@ private fun MessagesViewComposerBottomSheetContents( private fun CantSendMessageBanner() { Row( modifier = Modifier - .fillMaxWidth() - .background(ElementTheme.colors.bgSubtleSecondary) - .padding(16.dp), + .fillMaxWidth() + .background(ElementTheme.colors.bgSubtleSecondary) + .padding(16.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt index a69c6d7612..56bac5be33 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListStateProvider.kt @@ -15,6 +15,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.features.messages.impl.crypto.sendfailure.resolve.anUnsignedDeviceSendFailure import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.event.aStaticLocationMode import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemAudioContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent @@ -127,7 +128,7 @@ open class ActionListStateProvider : PreviewParameterProvider { anActionListState( target = ActionListState.Target.Success( event = aTimelineItemEvent( - content = aTimelineItemLocationContent(), + content = aTimelineItemLocationContent(mode = aStaticLocationMode()), timelineItemReactions = reactionsState ), sentTimeFull = "January 1, 1970 at 12:00 AM", @@ -140,7 +141,7 @@ open class ActionListStateProvider : PreviewParameterProvider { anActionListState( target = ActionListState.Target.Success( event = aTimelineItemEvent( - content = aTimelineItemLocationContent(), + content = aTimelineItemLocationContent(mode = aStaticLocationMode()), timelineItemReactions = reactionsState ), sentTimeFull = "January 1, 1970 at 12:00 AM", diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 0c58316b5e..63ce48e100 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -136,6 +136,7 @@ class ThreadedMessagesNode( fun navigateToSendLocation() fun navigateToCreatePoll() fun navigateToEditPoll(eventId: EventId) + fun navigateToCurrentLiveLocation() fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) fun navigateToDeveloperSettings() @@ -248,6 +249,11 @@ class ThreadedMessagesNode( callback.navigateToDeveloperSettings() } + override fun navigateToCurrentLiveLocation() { + // Shouldn't happen because LiveLocationSharingBanner is not shown in threads. + callback.navigateToCurrentLiveLocation() + } + override fun close() = navigateUp() @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index edd8d446dc..faa4aabe8e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvent @@ -94,6 +95,7 @@ class TimelinePresenter( private val roomCallStatePresenter: Presenter, private val featureFlagService: FeatureFlagService, private val analyticsService: AnalyticsService, + private val liveLocationShareManager: ActiveLiveLocationShareManager, ) : Presenter { private val tag = "TimelinePresenter" @@ -200,7 +202,9 @@ class TimelinePresenter( is TimelineEvent.EditPoll -> { navigator.navigateToEditPoll(event.pollStartId) } - is TimelineEvent.StopLiveLocationShare -> Unit + is TimelineEvent.StopLiveLocationShare -> sessionCoroutineScope.launch { + liveLocationShareManager.stopShare(room.roomId) + } is TimelineEvent.FocusOnEvent -> sessionCoroutineScope.launch { focusRequestState.value = FocusRequestState.Requested(event.eventId, event.debounce) delay(event.debounce) 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 5785627564..936028cbd6 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 @@ -783,7 +783,7 @@ private fun MessageEventBubbleContent( val content = content.ensureActiveLiveLocation() val shouldHide = content.mode is TimelineItemLocationContent.Mode.Live && content.mode.isActive && - content.mode.canStop + content.mode.isOwnUser if (shouldHide) TimestampPosition.Hidden else TimestampPosition.Overlay } is TimelineItemPollContent -> TimestampPosition.Below diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt index f00b6b0b5b..4ab4ee84a6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt @@ -13,14 +13,12 @@ import androidx.compose.foundation.border 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.fillMaxWidth import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -77,13 +75,14 @@ private fun LiveLocationOverlay( Row( modifier = modifier .fillMaxWidth() - .background(ElementTheme.colors.bgCanvasDefault.copy(alpha = 0.9f)) - .padding(horizontal = 8.dp, vertical = 8.dp), + .background(ElementTheme.colors.bgCanvasDefault.copy(alpha = 0.9f)), verticalAlignment = Alignment.CenterVertically, ) { val iconShape = RoundedCornerShape(8.dp) Box( modifier = Modifier + // Ensure this Box uses same spacings than the Stop IconButton. + .minimumInteractiveComponentSize() .size(32.dp) .border( width = 1.dp, @@ -120,7 +119,6 @@ private fun LiveLocationOverlay( ) } } - Spacer(Modifier.width(8.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = if (mode.isActive) { @@ -140,13 +138,16 @@ private fun LiveLocationOverlay( } } - if (mode.isActive && mode.canStop) { + if (mode.canStopSharing) { IconButton( onClick = onStopClick, colors = IconButtonDefaults.iconButtonColors( containerColor = ElementTheme.colors.bgCriticalPrimary, contentColor = ElementTheme.colors.iconOnSolidPrimary, - ) + ), + modifier = Modifier + .minimumInteractiveComponentSize() + .size(30.dp) ) { Icon( imageVector = CompoundIcons.Stop(), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/FakeTimelineItemPresenterFactories.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/FakeTimelineItemPresenterFactories.kt index 7c36521fc0..0d51d9f5b8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/FakeTimelineItemPresenterFactories.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/FakeTimelineItemPresenterFactories.kt @@ -8,7 +8,9 @@ package io.element.android.features.messages.impl.timeline.di +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent +import io.element.android.features.messages.impl.timeline.model.event.ensureActiveLiveLocation import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.voiceplayer.api.VoiceMessageState import io.element.android.libraries.voiceplayer.api.aVoiceMessageState @@ -18,6 +20,12 @@ import io.element.android.libraries.voiceplayer.api.aVoiceMessageState */ fun aFakeTimelineItemPresenterFactories() = TimelineItemPresenterFactories( mapOf( + Pair( + TimelineItemLocationContent::class, + TimelineItemPresenterFactory { content -> + Presenter { content.ensureActiveLiveLocation() } + }, + ), Pair( TimelineItemVoiceContent::class, TimelineItemPresenterFactory { Presenter { aVoiceMessageState() } }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index dff195e833..11f6668dfb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -127,6 +127,7 @@ class TimelineItemContentFactory( isActive = itemContent.isLive, endsAt = stringProvider.getString(CommonStrings.common_ends_at, endsAt), endTimestamp = itemContent.endTimestamp, + isOwnUser = sessionId == sender ), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt index 44dd2df38d..52e008e121 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt @@ -27,7 +27,7 @@ class TimelineItemEventContentProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aTimelineItemLocationContent(), aTimelineItemLocationContent( - mode = TimelineItemLocationContent.Mode.Live( - isActive = true, - endsAt = "Ends at 12:34", - endTimestamp = 0L, - canStop = true, - lastKnownLocation = aLocation() - ), + mode = aStaticLocationMode() ), aTimelineItemLocationContent( - mode = TimelineItemLocationContent.Mode.Live( - isActive = true, - endsAt = "Ends at 12:34", - endTimestamp = 0L, - lastKnownLocation = aLocation() - ), + mode = aLiveLocationMode(isActive = true) ), aTimelineItemLocationContent( - mode = TimelineItemLocationContent.Mode.Live( - isActive = true, - endsAt = "Ends at 12:34", - endTimestamp = 0L, - lastKnownLocation = null - ), + mode = aLiveLocationMode(isActive = true, lastKnownLocation = null) ), aTimelineItemLocationContent( - mode = TimelineItemLocationContent.Mode.Live( - isActive = false, - endsAt = "", - endTimestamp = 0L, - lastKnownLocation = aLocation() - ), + mode = aLiveLocationMode(isActive = true, isOwnUser = false) + ), + aTimelineItemLocationContent( + mode = aLiveLocationMode(isActive = false) ), ) } +fun aLiveLocationMode( + isActive: Boolean, + isOwnUser: Boolean = true, + lastKnownLocation: Location? = aLocation(), + endsAt: String = "Ends at 12:34", + endTimestamp: Long = 0L, +): TimelineItemLocationContent.Mode = TimelineItemLocationContent.Mode.Live( + isActive = isActive, + endsAt = endsAt, + endTimestamp = endTimestamp, + isOwnUser = isOwnUser, + lastKnownLocation = lastKnownLocation +) + +fun aStaticLocationMode(location: Location = aLocation()) = TimelineItemLocationContent.Mode.Static(location) fun aTimelineItemLocationContent( senderId: UserId = UserId("@sender:matrix.org"), senderProfile: ProfileDetails = aProfileDetailsReady(), description: String? = null, - mode: TimelineItemLocationContent.Mode = TimelineItemLocationContent.Mode.Static(aLocation()), + mode: TimelineItemLocationContent.Mode, ) = TimelineItemLocationContent( senderId = senderId, senderProfile = senderProfile, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index 44d82f1a7c..68f9a8d17a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -28,6 +28,7 @@ class FakeMessagesNavigator( private val navigateToDeveloperSettingsLambda: () -> Unit = { lambdaError() }, private val onOpenThreadLambda: (threadRootId: ThreadId, focusedEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, private val closeLambda: () -> Unit = { lambdaError() }, + private val navigateToCurrentLiveLocationLambda: () -> Unit = { lambdaError() }, ) : MessagesNavigator { override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickLambda(eventId, debugInfo) @@ -65,6 +66,10 @@ class FakeMessagesNavigator( navigateToDeveloperSettingsLambda() } + override fun navigateToCurrentLiveLocation() { + navigateToCurrentLiveLocationLambda() + } + override fun close() { closeLambda() } 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 50bacb005f..0c836e6cc0 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 @@ -13,6 +13,7 @@ package io.element.android.features.messages.impl import androidx.lifecycle.Lifecycle import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.PinUnpinAction +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.impl.actionlist.ActionListEvent import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.anActionListState @@ -120,6 +121,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds @Suppress("LargeClass") class MessagesPresenterTest { @@ -140,6 +142,39 @@ class MessagesPresenterTest { assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized) assertThat(initialState.showReinvitePrompt).isFalse() + assertThat(initialState.showLiveLocationShareBanner).isFalse() + } + } + + @Test + fun `present - exposes live location sharing banner visibility for current room`() = runTest { + val liveLocationShareManager = FakeActiveLiveLocationShareManager( + startShareLambda = { _, _ -> Result.success(Unit) }, + ) + liveLocationShareManager.startShare(A_ROOM_ID, 60.seconds) + val presenter = createMessagesPresenter(liveLocationShareManager = liveLocationShareManager) + + presenter.testWithLifecycleOwner { + val state = consumeItemsUntilTimeout().last() + assertThat(state.showLiveLocationShareBanner).isTrue() + } + } + + @Test + fun `present - stop live location share delegates to manager for current room`() = runTest { + val stopShareLambda = lambdaRecorder> { Result.success(Unit) } + val liveLocationShareManager = FakeActiveLiveLocationShareManager( + stopShareLambda = stopShareLambda + ) + val presenter = createMessagesPresenter(liveLocationShareManager = liveLocationShareManager) + + presenter.testWithLifecycleOwner { + val state = consumeItemsUntilTimeout().last() + state.eventSink(MessagesEvent.StopLiveLocationShare) + advanceUntilIdle() + assert(stopShareLambda) + .isCalledOnce() + .with(value(A_ROOM_ID)) } } @@ -1347,6 +1382,7 @@ class MessagesPresenterTest { actionListEventSink: (ActionListEvent) -> Unit = {}, addRecentEmoji: AddRecentEmoji = AddRecentEmoji { _ -> lambdaError() }, markAsFullyRead: MarkAsFullyRead = FakeMarkAsFullyRead(), + liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), ): MessagesPresenter { return MessagesPresenter( navigator = navigator, @@ -1376,6 +1412,7 @@ class MessagesPresenterTest { featureFlagService = featureFlagService, addRecentEmoji = addRecentEmoji, markAsFullyRead = markAsFullyRead, + liveLocationShareManager = liveLocationShareManager, sessionCoroutineScope = backgroundScope, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index 70ef70325e..be44c64a5a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -643,6 +643,44 @@ class MessagesViewTest { assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_message) assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_action) } + + @Test + fun `live location banner is visible when current room is sharing`() = runAndroidComposeUiTest { + val state = aMessagesState(isCurrentlySharingLiveLocationInRoom = true) + setMessagesView(state = state) + onNodeWithText(activity!!.getString(CommonStrings.screen_room_live_location_banner)).assertExists() + } + + @Test + fun `live location banner is hidden when current room is not sharing`() = runAndroidComposeUiTest { + val state = aMessagesState(isCurrentlySharingLiveLocationInRoom = false) + setMessagesView(state = state) + onNodeWithText(activity!!.getString(CommonStrings.screen_room_live_location_banner)).assertDoesNotExist() + } + + @Test + fun `clicking stop on live location banner emits expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + isCurrentlySharingLiveLocationInRoom = true, + eventSink = eventsRecorder, + ) + setMessagesView(state = state) + clickOn(CommonStrings.action_stop) + eventsRecorder.assertSingle(MessagesEvent.StopLiveLocationShare) + } + + @Test + fun `clicking live location banner emit expected event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + val state = aMessagesState( + isCurrentlySharingLiveLocationInRoom = true, + eventSink = eventsRecorder, + ) + setMessagesView(state = state) + clickOn(CommonStrings.screen_room_live_location_banner) + eventsRecorder.assertSingle(MessagesEvent.ShowLiveLocationShare) + } } private fun AndroidComposeUiTest.setMessagesView( 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 194694714b..af37fb61ef 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 @@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.timeline import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.impl.FakeMessagesNavigator import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aResolveVerifiedUserSendFailureState import io.element.android.features.messages.impl.fixtures.aMessageEvent @@ -1012,6 +1013,7 @@ class TimelinePresenterTest { sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), + liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), ): TimelinePresenter { return TimelinePresenter( timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), @@ -1030,6 +1032,7 @@ class TimelinePresenterTest { roomCallStatePresenter = { aStandByCallState() }, featureFlagService = featureFlagService, analyticsService = FakeAnalyticsService(), + liveLocationShareManager = liveLocationShareManager, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index b3fb68fe05..ef27c499af 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -20,4 +20,5 @@ sealed interface AdvancedSettingsEvents { data class SetTheme(val theme: ThemeOption) : AdvancedSettingsEvents data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvents data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvents + data class SetLiveLocationMinimumDistanceUpdate(val value: Int) : AdvancedSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt index e58706e9fe..96b1cddc94 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsNode.kt @@ -10,12 +10,14 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.androidutils.system.openAppSettingsPage import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -28,10 +30,12 @@ class AdvancedSettingsNode( @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val context = LocalContext.current AdvancedSettingsView( state = state, modifier = modifier, - onBackClick = ::navigateUp + onBackClick = ::navigateUp, + onOpenAppSettingsClick = context::openAppSettingsPage ) } } 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 ae32bb01a5..3d1fb3b0c7 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 @@ -25,8 +25,11 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch @Inject @@ -53,6 +56,19 @@ class AdvancedSettingsPresenter( appPreferencesStore.getThemeFlow().mapToTheme(isBlackThemeAllowed) }.collectAsState(initial = Theme.System) + @OptIn(ExperimentalCoroutinesApi::class) + val liveLocationMinimumDistanceUpdate by produceState(null) { + featureFlagService.isFeatureEnabledFlow(FeatureFlags.LiveLocationSharing) + .flatMapLatest { isEnabled -> + if (isEnabled) { + appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow() + } else { + emptyFlow() + } + } + .collect { value = it } + } + val mediaPreviewConfigState = mediaPreviewConfigStateStore.state() val themeOption by remember { @@ -117,6 +133,9 @@ class AdvancedSettingsPresenter( } is AdvancedSettingsEvents.SetHideInviteAvatars -> mediaPreviewConfigStateStore.setHideInviteAvatars(event.value) is AdvancedSettingsEvents.SetTimelineMediaPreviewValue -> mediaPreviewConfigStateStore.setTimelineMediaPreviewValue(event.value) + is AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate -> sessionCoroutineScope.launch { + appPreferencesStore.setLiveLocationMinimumDistanceInMetersUpdate(event.value) + } is AdvancedSettingsEvents.SetCompressImages -> sessionCoroutineScope.launch { sessionPreferencesStore.setOptimizeImages(event.compress) } @@ -133,6 +152,7 @@ class AdvancedSettingsPresenter( theme = themeOption, availableThemeOptions = availableThemeOptions, mediaPreviewConfigState = mediaPreviewConfigState, + liveLocationMinimumDistanceUpdate = liveLocationMinimumDistanceUpdate, eventSink = ::handleEvent, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 0525130048..1deb972abc 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -23,6 +23,7 @@ data class AdvancedSettingsState( val theme: ThemeOption, val availableThemeOptions: ImmutableList, val mediaPreviewConfigState: MediaPreviewConfigState, + val liveLocationMinimumDistanceUpdate: Int?, val eventSink: (AdvancedSettingsEvents) -> Unit ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index 87df614074..2df59e3ced 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -41,6 +41,7 @@ fun aAdvancedSettingsState( availableThemeOptions: ImmutableList = ThemeOption.entries.toImmutableList(), hideInviteAvatars: Boolean = false, timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On, + liveLocationMinimumDistanceUpdate: Int? = 50, setTimelineMediaPreviewAction: AsyncAction = AsyncAction.Uninitialized, setHideInviteAvatarsAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (AdvancedSettingsEvents) -> Unit = {}, @@ -56,5 +57,6 @@ fun aAdvancedSettingsState( setTimelineMediaPreviewAction = setTimelineMediaPreviewAction, setHideInviteAvatarsAction = setHideInviteAvatarsAction ), + liveLocationMinimumDistanceUpdate = liveLocationMinimumDistanceUpdate, eventSink = eventSink ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 230f5fa739..af28e3443f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -8,15 +8,24 @@ package io.element.android.features.preferences.impl.advanced +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp import im.vector.app.features.analytics.plan.Interaction import io.element.android.compound.theme.ElementTheme import io.element.android.features.preferences.impl.R @@ -33,10 +42,12 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.stringWithLink import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ListSectionHeader import io.element.android.libraries.designsystem.theme.components.ListSupportingText import io.element.android.libraries.designsystem.theme.components.ListSupportingTextDefaults +import io.element.android.libraries.designsystem.theme.components.Slider import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost @@ -47,11 +58,13 @@ import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.compose.LocalAnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction +import kotlin.math.roundToInt @Composable fun AdvancedSettingsView( state: AdvancedSettingsState, onBackClick: () -> Unit, + onOpenAppSettingsClick: () -> Unit, modifier: Modifier = Modifier, ) { val analyticsService = LocalAnalyticsService.current @@ -190,6 +203,15 @@ fun AdvancedSettingsView( } ModerationAndSafety(state) + if (state.liveLocationMinimumDistanceUpdate != null) { + LiveLocationUpdatesSection( + value = state.liveLocationMinimumDistanceUpdate, + onValueSaved = { value -> + state.eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(value)) + }, + onOpenAppPermissionsClick = onOpenAppSettingsClick, + ) + } } } @@ -314,6 +336,78 @@ private fun ModerationAndSafety( } } +@Composable +private fun LiveLocationUpdatesSection( + value: Int, + onValueSaved: (Int) -> Unit, + onOpenAppPermissionsClick: () -> Unit, + modifier: Modifier = Modifier, +) { + PreferenceCategory( + modifier = modifier, + showTopDivider = true, + ) { + ListSectionHeader( + title = stringResource(R.string.screen_advanced_settings_live_location_section_title), + description = { + ListSupportingText( + text = stringResource(R.string.screen_advanced_settings_live_location_section_description), + contentPadding = ListSupportingTextDefaults.Padding.None, + ) + } + ) + var sliderValue by remember(value) { mutableIntStateOf(value) } + Column( + modifier = Modifier.padding(vertical = 12.dp, horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = pluralStringResource( + R.plurals.screen_advanced_settings_live_location_update_distance, + sliderValue, + sliderValue, + ), + style = ElementTheme.typography.fontBodyLgRegular, + color = ElementTheme.colors.textPrimary, + ) + val valueRange = 1f..100f + val start = valueRange.start.toInt() + val end = valueRange.endInclusive.toInt() + Row(verticalAlignment = Alignment.CenterVertically) { + Text("${start}m", color = ElementTheme.colors.textSecondary, style = ElementTheme.typography.fontBodyMdRegular) + Slider( + modifier = Modifier + .weight(1f) + .padding(horizontal = 12.dp), + value = sliderValue.toFloat(), + onValueChange = { sliderValue = it.roundToInt() }, + onValueChangeFinish = { + onValueSaved(sliderValue) + }, + valueRange = valueRange, + colors = SliderDefaults.colors( + thumbColor = ElementTheme.colors.iconAccentPrimary, + activeTrackColor = ElementTheme.colors.iconAccentPrimary, + inactiveTrackColor = ElementTheme.colors.bgBadgeAccent, + inactiveTickColor = ElementTheme.colors.iconAccentPrimary, + ) + ) + Text("${end}m", color = ElementTheme.colors.textSecondary, style = ElementTheme.typography.fontBodyMdRegular) + } + } + val footerText = stringWithLink( + textRes = R.string.screen_advanced_settings_live_location_section_footer, + url = "", + linkTextRes = R.string.screen_advanced_settings_live_location_section_footer_link, + onLinkClick = { onOpenAppPermissionsClick() }, + ) + ListSupportingText( + annotatedString = footerText, + contentPadding = ListSupportingTextDefaults.Padding.Default, + ) + } +} + @PreviewWithLargeHeight @Composable internal fun AdvancedSettingsViewLightPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) = @@ -334,7 +428,8 @@ internal fun AdvancedSettingsViewBlackPreview(@PreviewParameter(AdvancedSettings private fun ContentToPreview(state: AdvancedSettingsState) { AdvancedSettingsView( state = state, - onBackClick = { } + onBackClick = { }, + onOpenAppSettingsClick = {} ) } 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 fe121e4fe9..27d91bd8de 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 @@ -17,6 +17,7 @@ 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.media.MediaPreviewValue +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.VideoCompressionPreset import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore @@ -209,6 +210,72 @@ class AdvancedSettingsPresenterTest { } } + @Test + fun `present - live location minimum distance is null when feature is disabled`() = runTest { + val appPreferencesStore = InMemoryAppPreferencesStore( + liveLocationMinimumDistanceUpdate = 50, + ) + val featureFlagService = FakeFeatureFlagService().apply { + setFeatureEnabled(FeatureFlags.LiveLocationSharing, false) + } + val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + with(awaitItem()) { + assertThat(liveLocationMinimumDistanceUpdate).isNull() + } + } + } + + @Test + fun `present - exposes live location minimum distance from app preferences`() = runTest { + val appPreferencesStore = InMemoryAppPreferencesStore( + liveLocationMinimumDistanceUpdate = 50, + ) + val featureFlagService = FakeFeatureFlagService().apply { + setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) + } + val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + + with(awaitItem()) { + assertThat(liveLocationMinimumDistanceUpdate).isEqualTo(50) + } + } + } + + @Test + fun `present - saving live location minimum distance updates app preferences`() = runTest { + val appPreferencesStore = InMemoryAppPreferencesStore( + liveLocationMinimumDistanceUpdate = 10, + ) + val featureFlagService = FakeFeatureFlagService().apply { + setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) + } + val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) + + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + + with(awaitItem()) { + assertThat(liveLocationMinimumDistanceUpdate).isEqualTo(10) + eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(42)) + } + with(awaitItem()) { + assertThat(liveLocationMinimumDistanceUpdate).isEqualTo(42) + } + } + } + @Test fun `present - black theme option shown when feature flag enabled`() = runTest { val presenter = createAdvancedSettingsPresenter( @@ -338,7 +405,7 @@ class AdvancedSettingsPresenterTest { } private fun CoroutineScope.createAdvancedSettingsPresenter( - appPreferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), mediaPreviewConfigStateStore: MediaPreviewConfigStateStore = FakeMediaPreviewConfigStateStore(), featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt index b6fe5c3d0b..4823a7c26e 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt @@ -250,6 +250,7 @@ private fun AndroidComposeUiTest.setAdvancedSettingsView( state: AdvancedSettingsState, analyticsService: AnalyticsService = FakeAnalyticsService(), onBackClick: () -> Unit = EnsureNeverCalled(), + onOpenAppSettings: () -> Unit = EnsureNeverCalled(), ) { setContent { CompositionLocalProvider( @@ -258,6 +259,7 @@ private fun AndroidComposeUiTest.setAdvancedSettingsView( AdvancedSettingsView( state = state, onBackClick = onBackClick, + onOpenAppSettingsClick = onOpenAppSettings ) } } 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 59fd7a4940..bd7b1399e1 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 @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.NotJoinedRoom import io.element.android.libraries.matrix.api.room.RoomInfo 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.location.BeaconInfoUpdate import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.spaces.SpaceService @@ -67,6 +68,7 @@ interface MatrixClient { val sessionCoroutineScope: CoroutineScope val ignoredUsersFlow: StateFlow> val roomMembershipObserver: RoomMembershipObserver + val ownBeaconInfoUpdates: Flow suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? suspend fun getRoom(roomId: RoomId): BaseRoom? suspend fun findDM(userId: UserId): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconId.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconId.kt new file mode 100644 index 0000000000..358dcd98d1 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconId.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.room.location + +import io.element.android.libraries.matrix.api.core.EventId + +typealias BeaconId = EventId diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconInfoUpdate.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconInfoUpdate.kt new file mode 100644 index 0000000000..0b7e9b0f44 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/BeaconInfoUpdate.kt @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.room.location + +import io.element.android.libraries.matrix.api.core.RoomId + +data class BeaconInfoUpdate( + val roomId: RoomId, + val beaconId: BeaconId, + val isLive: Boolean, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationException.kt new file mode 100644 index 0000000000..9b53603042 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationException.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.room.location + +sealed class LiveLocationException(message: String?) : Exception(message) { + class NotLive : LiveLocationException("The beacon event has expired.") + class Network : LiveLocationException("Network error") + class Other(val exception: Exception) : LiveLocationException(exception.message) +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt index 3f9c108dc7..4d8bc4638a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/location/LiveLocationShare.kt @@ -21,6 +21,8 @@ data class LiveLocationShare( val startTimestamp: Long, /** The timestamp when location sharing ends, in milliseconds. */ val endTimestamp: Long, + /** The event id from the beacon info. */ + val beaconId: BeaconId ) data class LastLocation( 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 5a1dc9da4b..05e52605dc 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 @@ -70,6 +70,7 @@ import io.element.android.libraries.matrix.impl.room.RustRoomFactory import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map +import io.element.android.libraries.matrix.impl.room.location.map import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService import io.element.android.libraries.matrix.impl.roomdirectory.map @@ -113,6 +114,8 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.AuthData import org.matrix.rustcomponents.sdk.AuthDataPasswordDetails +import org.matrix.rustcomponents.sdk.BeaconInfoListener +import org.matrix.rustcomponents.sdk.BeaconInfoUpdate import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientException import org.matrix.rustcomponents.sdk.IgnoredUsersListener @@ -207,6 +210,15 @@ class RustMatrixClient( analyticsService = analyticsService, ) + override val ownBeaconInfoUpdates = mxCallbackFlow { + val listener = object : BeaconInfoListener { + override fun onUpdate(update: BeaconInfoUpdate) { + trySend(update.map()) + } + } + innerClient.subscribeToOwnBeaconInfoUpdates(listener) + } + override val sessionVerificationService = RustSessionVerificationService( client = innerClient, isSyncServiceReady = syncService.syncState.map { it == SyncState.Running }, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index 87ef0815ce..caeef02e8e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -45,6 +45,7 @@ import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.knock.RustKnockRequest import io.element.android.libraries.matrix.impl.room.location.liveLocationSharesFlow +import io.element.android.libraries.matrix.impl.room.location.map import io.element.android.libraries.matrix.impl.room.location.timedByExpiry import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.room.threads.RustThreadsListService @@ -72,6 +73,7 @@ import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.DateDividerMode import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener import org.matrix.rustcomponents.sdk.KnockRequestsListener +import org.matrix.rustcomponents.sdk.LiveLocationException import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType import org.matrix.rustcomponents.sdk.RoomSendQueueUpdate import org.matrix.rustcomponents.sdk.SendQueueListener @@ -525,12 +527,22 @@ class JoinedRustRoom( override suspend fun stopLiveLocationShare(): Result = withContext(roomDispatcher) { runCatchingExceptions { innerRoom.stopLiveLocationShare() + }.mapFailure { throwable -> + when (throwable) { + is LiveLocationException -> throwable.map() + else -> throwable + } } } override suspend fun sendLiveLocation(geoUri: String): Result = withContext(roomDispatcher) { runCatchingExceptions { innerRoom.sendLiveLocation(geoUri) + }.mapFailure { throwable -> + when (throwable) { + is LiveLocationException -> throwable.map() + else -> throwable + } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/BeaconInfoUpdates.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/BeaconInfoUpdates.kt new file mode 100644 index 0000000000..44be305c02 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/BeaconInfoUpdates.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.room.location + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.location.BeaconInfoUpdate +import org.matrix.rustcomponents.sdk.BeaconInfoUpdate as RustBeaconInfoUpdate + +fun RustBeaconInfoUpdate.map(): BeaconInfoUpdate { + return BeaconInfoUpdate( + roomId = RoomId(roomId), + beaconId = EventId(eventId), + isLive = live + ) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationException.kt new file mode 100644 index 0000000000..b10f5cd41c --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationException.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.room.location + +import io.element.android.libraries.matrix.api.room.location.LiveLocationException +import org.matrix.rustcomponents.sdk.LiveLocationException as RustLiveLocationException + +fun RustLiveLocationException.map(): LiveLocationException { + return when (this) { + is RustLiveLocationException.Network -> LiveLocationException.Network() + is RustLiveLocationException.NotLive -> LiveLocationException.NotLive() + else -> LiveLocationException.Other(this) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt index 1a341d0dc2..8e8181539f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/location/LiveLocationSharesFlow.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.impl.room.location +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.room.location.LastLocation import io.element.android.libraries.matrix.api.room.location.LiveLocationShare @@ -41,9 +42,9 @@ fun RoomInterface.liveLocationSharesFlow(): Flow> { } } return callbackFlow { - val liveLocationShares = liveLocationsObserver() + val observer = liveLocationsObserver() val shares: MutableList = ArrayList() - val taskHandle = liveLocationShares.subscribe(object : LiveLocationsListener { + val taskHandle = observer.subscribe(object : LiveLocationsListener { override fun onUpdate(updates: List) { for (update in updates) { shares.applyUpdate(update) @@ -53,13 +54,14 @@ fun RoomInterface.liveLocationSharesFlow(): Flow> { }) awaitClose { taskHandle.cancelAndDestroy() - liveLocationShares.destroy() + observer.destroy() } }.buffer(Channel.UNLIMITED) } private fun RustLiveLocationShare.into(): LiveLocationShare { return LiveLocationShare( + beaconId = EventId(beaconId), userId = UserId(userId), lastLocation = lastLocation?.let { LastLocation( @@ -69,6 +71,6 @@ private fun RustLiveLocationShare.into(): LiveLocationShare { ) }, startTimestamp = startTs.toLong(), - endTimestamp = (startTs + timeout).toLong() + endTimestamp = (startTs + timeout).toLong(), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/location/TimedLiveLocationSharesFlowTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/location/TimedLiveLocationSharesFlowTest.kt index 41627396ad..ba91dae468 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/location/TimedLiveLocationSharesFlowTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/location/TimedLiveLocationSharesFlowTest.kt @@ -11,6 +11,7 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.emptyFlow @@ -24,9 +25,9 @@ class TimedLiveLocationSharesFlowTest { @Test fun `it keeps emitting shares for subsequent expiries without upstream changes`() = runTest { val shares = listOf( - aLiveLocationShare(userId = "@alice:server", endTimestamp = 1_000), - aLiveLocationShare(userId = "@bob:server", endTimestamp = 2_000), - aLiveLocationShare(userId = "@carol:server", endTimestamp = 3_000), + aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 1_000), + aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 2_000), + aLiveLocationShare(userId = UserId("@carol:server"), endTimestamp = 3_000), ) flowOf(shares) @@ -56,8 +57,8 @@ class TimedLiveLocationSharesFlowTest { @Test fun `it does not double-emit when a share is already expired on receipt`() = runTest { val shares = listOf( - aLiveLocationShare(userId = "@alice:server", endTimestamp = 500), - aLiveLocationShare(userId = "@bob:server", endTimestamp = 2_000), + aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 500), + aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 2_000), ) flowOf(shares) @@ -81,8 +82,8 @@ class TimedLiveLocationSharesFlowTest { val upstream = MutableSharedFlow>(extraBufferCapacity = 1) val initialShares = listOf(aLiveLocationShare(endTimestamp = 10_000)) val updatedShares = listOf( - aLiveLocationShare(userId = "@alice:server", endTimestamp = 10_000), - aLiveLocationShare(userId = "@bob:server", endTimestamp = 6_000), + aLiveLocationShare(userId = UserId("@alice:server"), endTimestamp = 10_000), + aLiveLocationShare(userId = UserId("@bob:server"), endTimestamp = 6_000), ) upstream @@ -133,15 +134,3 @@ class TimedLiveLocationSharesFlowTest { } } } - -private fun aLiveLocationShare( - userId: String = "@user:server", - endTimestamp: Long, -): LiveLocationShare { - return LiveLocationShare( - userId = UserId(userId), - lastLocation = null, - startTimestamp = 0L, - endTimestamp = endTimestamp, - ) -} 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 26bc0af8c1..af28dc37e4 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 @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.NotJoinedRoom import io.element.android.libraries.matrix.api.room.RoomInfo 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.location.BeaconInfoUpdate import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.spaces.SpaceService @@ -107,6 +108,7 @@ class FakeMatrixClient( private val canReportRoomLambda: () -> Boolean = { false }, private val isLivekitRtcSupportedLambda: () -> Boolean = { false }, override val ignoredUsersFlow: StateFlow> = MutableStateFlow(persistentListOf()), + override val ownBeaconInfoUpdates: Flow = emptyFlow(), private val getMaxUploadSizeResult: () -> Result = { lambdaError() }, private val getJoinedRoomIdsResult: () -> Result> = { Result.success(emptySet()) }, private val getRecentEmojisLambda: () -> Result> = { Result.success(emptyList()) }, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt index d1cd340641..9f7255ab18 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt @@ -89,7 +89,7 @@ class FakeJoinedRoom( private val updateJoinRuleResult: (JoinRule) -> Result = { lambdaError() }, private val setSendQueueEnabledResult: (Boolean) -> Unit = { _: Boolean -> }, private val liveLocationSharesFlow: Flow> = MutableStateFlow(emptyList()), - private val startLiveLocationShareResult: (Long) -> Result = { lambdaError() }, + private val startLiveLocationShareResult: (Long) -> Result = { lambdaError() }, private val stopLiveLocationShareResult: () -> Result = { lambdaError() }, private val sendLiveLocationResult: (String) -> Result = { lambdaError() }, ) : JoinedRoom, BaseRoom by baseRoom { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/location/LiveLocationFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/location/LiveLocationFixture.kt new file mode 100644 index 0000000000..23730c8886 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/location/LiveLocationFixture.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.test.room.location + +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.room.location.AssetType +import io.element.android.libraries.matrix.api.room.location.LastLocation +import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID + +fun aLiveLocationShare( + beaconId: EventId = AN_EVENT_ID, + userId: UserId = A_USER_ID, + geoUri: String = "geo:48.8584,2.2945", + timestamp: Long = 0L, + startTimestamp: Long = 0L, + endTimestamp: Long = Long.MAX_VALUE, + assetType: AssetType = AssetType.SENDER, +): LiveLocationShare { + return LiveLocationShare( + beaconId = beaconId, + userId = userId, + lastLocation = LastLocation( + geoUri = geoUri, + timestamp = timestamp, + assetType = assetType, + ), + startTimestamp = startTimestamp, + endTimestamp = endTimestamp, + ) +} diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index 476658946a..df8dc8acc9 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -23,6 +23,9 @@ interface AppPreferencesStore { suspend fun setTheme(theme: String) fun getThemeFlow(): Flow + suspend fun setLiveLocationMinimumDistanceInMetersUpdate(value: Int) + fun getLiveLocationMinimumDistanceInMetersUpdateFlow(): Flow + @Deprecated("Use MediaPreviewService instead. Kept only for migration.") suspend fun setHideInviteAvatars(hide: Boolean?) @Deprecated("Use MediaPreviewService instead. Kept only for migration.") diff --git a/libraries/preferences/impl/build.gradle.kts b/libraries/preferences/impl/build.gradle.kts index 0478d303ea..73327a69a2 100644 --- a/libraries/preferences/impl/build.gradle.kts +++ b/libraries/preferences/impl/build.gradle.kts @@ -1,4 +1,5 @@ import extension.setupDependencyInjection +import extension.testCommonDependencies /* * Copyright (c) 2025 Element Creations Ltd. @@ -26,4 +27,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.sessionStorage.api) + testCommonDependencies(libs) + testImplementation(projects.libraries.preferences.test) } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index 6856f8bdb6..44260461da 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.preferences.impl.store import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding @@ -28,6 +29,7 @@ private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseU private val themeKey = stringPreferencesKey("theme") private val hideInviteAvatarsKey = booleanPreferencesKey("hideInviteAvatars") private val timelineMediaPreviewValueKey = stringPreferencesKey("timelineMediaPreviewValue") +private val liveLocationMinimumDistanceUpdateKey = intPreferencesKey("liveLocationMinimumDistanceUpdate") private val logLevelKey = stringPreferencesKey("logLevel") private val traceLogPacksKey = stringPreferencesKey("traceLogPacks") @@ -79,6 +81,18 @@ class DefaultAppPreferencesStore( } } + override suspend fun setLiveLocationMinimumDistanceInMetersUpdate(value: Int) { + store.edit { prefs -> + prefs[liveLocationMinimumDistanceUpdateKey] = value + } + } + + override fun getLiveLocationMinimumDistanceInMetersUpdateFlow(): Flow { + return store.data.map { prefs -> + prefs[liveLocationMinimumDistanceUpdateKey] ?: 10 + } + } + @Deprecated("Use MediaPreviewService instead. Kept only for migration.") override fun getHideInviteAvatarsFlow(): Flow { return store.data.map { prefs -> diff --git a/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt b/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt new file mode 100644 index 0000000000..c52d1648ac --- /dev/null +++ b/libraries/preferences/impl/src/test/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStoreTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.preferences.impl.store + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.core.meta.BuildType +import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFactory +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DefaultAppPreferencesStoreTest { + private val buildMeta = BuildMeta( + buildType = BuildType.DEBUG, + isDebuggable = true, + applicationName = "Element X", + productionApplicationName = "Element", + desktopApplicationName = "Element Desktop", + applicationId = "io.element.android", + isEnterpriseBuild = false, + lowPrivacyLoggingEnabled = false, + versionName = "1.0.0", + versionCode = 1, + gitRevision = "test", + gitBranchName = "test", + flavorDescription = "test", + flavorShortDescription = "test", + ) + + @Test + fun `live location minimum distance defaults to 10`() = runTest { + val store = DefaultAppPreferencesStore( + buildMeta = buildMeta, + preferenceDataStoreFactory = FakePreferenceDataStoreFactory(), + ) + + assertThat(store.getLiveLocationMinimumDistanceInMetersUpdateFlow().first()).isEqualTo(10) + } + + @Test + fun `live location minimum distance persists updates`() = runTest { + val store = DefaultAppPreferencesStore( + buildMeta = buildMeta, + preferenceDataStoreFactory = FakePreferenceDataStoreFactory(), + ) + + store.setLiveLocationMinimumDistanceInMetersUpdate(25) + + assertThat(store.getLiveLocationMinimumDistanceInMetersUpdateFlow().first()).isEqualTo(25) + } +} diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt index 6e7d22a568..152de12e99 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt @@ -21,12 +21,14 @@ class InMemoryAppPreferencesStore( hideInviteAvatars: Boolean? = null, timelineMediaPreviewValue: MediaPreviewValue? = null, theme: String? = null, + liveLocationMinimumDistanceUpdate: Int = 10, logLevel: LogLevel = LogLevel.INFO, traceLockPacks: Set = emptySet(), ) : AppPreferencesStore { private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled) private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl) private val theme = MutableStateFlow(theme) + private val liveLocationMinimumDistanceUpdate = MutableStateFlow(liveLocationMinimumDistanceUpdate) private val logLevel = MutableStateFlow(logLevel) private val tracingLogPacks = MutableStateFlow(traceLockPacks) private val hideInviteAvatars = MutableStateFlow(hideInviteAvatars) @@ -56,6 +58,14 @@ class InMemoryAppPreferencesStore( return theme } + override suspend fun setLiveLocationMinimumDistanceInMetersUpdate(value: Int) { + liveLocationMinimumDistanceUpdate.value = value + } + + override fun getLiveLocationMinimumDistanceInMetersUpdateFlow(): Flow { + return liveLocationMinimumDistanceUpdate + } + @Deprecated("Use MediaPreviewService instead. Kept only for migration.") override fun getHideInviteAvatarsFlow(): Flow { return hideInviteAvatars diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationIdProvider.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationIdProvider.kt index ff7119b647..367052eaad 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationIdProvider.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationIdProvider.kt @@ -53,4 +53,5 @@ object NotificationIdProvider { enum class ForegroundServiceType { INCOMING_CALL, ONGOING_CALL, + LIVE_LOCATION, } diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt index fdf5cc5f1b..a7c3a6837b 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/FakeSessionObserver.kt @@ -10,12 +10,13 @@ package io.element.android.libraries.sessionstorage.test.observer import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import java.util.concurrent.CopyOnWriteArraySet class FakeSessionObserver : SessionObserver { - private val _listeners = mutableListOf() + private val _listeners = CopyOnWriteArraySet() val listeners: List - get() = _listeners + get() = _listeners.toList() override fun addListener(listener: SessionListener) { _listeners.add(listener) diff --git a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt index d6effec06f..33c2ee133b 100644 --- a/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt +++ b/services/appnavstate/api/src/main/kotlin/io/element/android/services/appnavstate/api/AppForegroundStateService.kt @@ -34,6 +34,8 @@ interface AppForegroundStateService { */ val isSyncingNotificationEvent: StateFlow + val isSharingLiveLocation: StateFlow + /** * Start observing the foreground state. */ @@ -53,4 +55,6 @@ interface AppForegroundStateService { * Update the active state for the syncing notification event flow. */ fun updateIsSyncingNotificationEvent(isSyncingNotificationEvent: Boolean) + + fun updateIsSharingLiveLocation(isSharingLiveLocation: Boolean) } diff --git a/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppForegroundStateService.kt b/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppForegroundStateService.kt index c9fa31caca..9dd37af732 100644 --- a/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppForegroundStateService.kt +++ b/services/appnavstate/impl/src/main/kotlin/io/element/android/services/appnavstate/impl/DefaultAppForegroundStateService.kt @@ -20,6 +20,8 @@ class DefaultAppForegroundStateService : AppForegroundStateService { override val isSyncingNotificationEvent = MutableStateFlow(false) override val hasRingingCall = MutableStateFlow(false) + override val isSharingLiveLocation = MutableStateFlow(false) + private val appLifecycle: Lifecycle by lazy { ProcessLifecycleOwner.get().lifecycle } override fun startObservingForeground() { @@ -38,6 +40,10 @@ class DefaultAppForegroundStateService : AppForegroundStateService { this.isSyncingNotificationEvent.value = isSyncingNotificationEvent } + override fun updateIsSharingLiveLocation(isSharingLiveLocation: Boolean) { + this.isSharingLiveLocation.value = isSharingLiveLocation + } + private val lifecycleObserver = LifecycleEventObserver { _, _ -> isInForeground.value = getCurrentState() } private fun getCurrentState(): Boolean = appLifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) diff --git a/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppForegroundStateService.kt b/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppForegroundStateService.kt index a61733cc22..9174b92684 100644 --- a/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppForegroundStateService.kt +++ b/services/appnavstate/test/src/main/kotlin/io/element/android/services/appnavstate/test/FakeAppForegroundStateService.kt @@ -16,12 +16,15 @@ class FakeAppForegroundStateService( initialIsInCallValue: Boolean = false, initialIsSyncingNotificationEventValue: Boolean = false, initialHasRingingCall: Boolean = false, + initialIsSharingLiveLocation: Boolean = false, ) : AppForegroundStateService { override val isInForeground = MutableStateFlow(initialForegroundValue) override val isInCall = MutableStateFlow(initialIsInCallValue) override val isSyncingNotificationEvent = MutableStateFlow(initialIsSyncingNotificationEventValue) override val hasRingingCall = MutableStateFlow(initialHasRingingCall) + override val isSharingLiveLocation = MutableStateFlow(initialIsSharingLiveLocation) + override fun startObservingForeground() { // No-op } @@ -41,4 +44,8 @@ class FakeAppForegroundStateService( override fun updateHasRingingCall(hasRingingCall: Boolean) { this.hasRingingCall.value = hasRingingCall } + + override fun updateIsSharingLiveLocation(isSharingLiveLocation: Boolean) { + this.isSharingLiveLocation.value = isSharingLiveLocation + } } diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Day_0_en.png new file mode 100644 index 0000000000..c431ee02e9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aaf113646fa3a8ffd57528d3e97eec05a8d80b99a3bbd7770266353dbe6abd64 +size 10217 diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Night_0_en.png new file mode 100644 index 0000000000..e3a3776919 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.api_LiveLocationSharingBanner_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d9deec2a6295fe0155ce2986463a3b598436c0515ef87e0905c62c25970420 +size 9647 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Day_0_en.png index cae8f75b66..afd74655d9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaafea9efc1000495ee469797239b82193844caa3d6f98c0c3a4344a536a1798 -size 17155 +oid sha256:c9ebf3725fa875994cf1a15b30e2d4d533c2a9281f0f7d5d9f83f8685ba384d1 +size 18637 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Night_0_en.png index 29f70fc9b1..30fad74bc6 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.common.ui_LocationShareRow_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f113f8979679c0673e4cc1f691140bc570b6826bea23eecc403f2fbfd3f6d09 -size 16460 +oid sha256:65c321199578618012d27afe478c0eaf6a67c44101e7d1d1c51a4c6c1fa9b93a +size 17897 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png index cce7a48382..0c9a67252c 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a97492422d54a6d6666c1ade693dc9b63bc9ca07c17d6c1f787c081984c09f68 -size 42470 +oid sha256:9f3fb75974ce37fc3ce5e303ab5573a0d9a769f3c079de5b0f6462b40e53c1cd +size 26713 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png new file mode 100644 index 0000000000..e8ae396119 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ddaf978cdf3e70b01fbee75e7e7290fa390e749b48ae192fe7da0dcaa9a1d4dc +size 38417 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png new file mode 100644 index 0000000000..cce7a48382 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a97492422d54a6d6666c1ade693dc9b63bc9ca07c17d6c1f787c081984c09f68 +size 42470 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png index 541b2a97e1..06ec0c10e9 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:038e12f3caeef6ac8d5389b7cdc68138e089dcac335d0a5904adc55c9bcb7b1c -size 40642 +oid sha256:4031ae26bb8465a61021c15b6166a2186f8f99d7a4dbad7bb28aaf83493cbe69 +size 25907 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png new file mode 100644 index 0000000000..7761c87d2e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9ed7610fbdabb3e88c35e2832d06566431d472ad95890c2fdcbb6d1b43d8feb +size 36751 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png new file mode 100644 index 0000000000..541b2a97e1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:038e12f3caeef6ac8d5389b7cdc68138e089dcac335d0a5904adc55c9bcb7b1c +size 40642 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png index bdb2a689ac..def7635efc 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82ed52f907048490ffb63ea9b33274703c1d92d4131cca0320816cc6949bea5e -size 113727 +oid sha256:b9f2f0e8ba6829cb3d83dcc0c6d3ed5a91a0346771f84adb6731ba5fdac01f4d +size 122009 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en.png index 5d9f6ab2ba..c6a916891b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae3a754c163f83e69ab062c8e638a6e620948081d70105ddb00c32dda7c2ba74 -size 119927 +oid sha256:459141f4442bade537f755d56fe0b4569980a26b92f23409c477802462084b3c +size 122118 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png index 01d01e6806..07613eb447 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:757d555f948a637952aed4b0ad2745212f6a9ab279d172b0a1649fdf547c5a0c -size 120027 +oid sha256:7f85b9e88dca466b567769291fb69afac535135c3a07f412d6b8203968460519 +size 120940 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png index 6962ed5f4d..f06f86068a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb0d86877e5d049d618836846cd8ea97b40d1d66e7bd8d50639280fb3e829372 -size 38499 +oid sha256:00da192b3b46897c9d50986ecd84e07332cd2b490ebc8f3893a9497e72f18af8 +size 41478 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en.png index 7b0ba78a2c..140a3c3446 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b41879f5159a126b0641c66bed13470756a914cc038dd239d40d359661784b71 -size 40696 +oid sha256:c5a9f9104c9544e32ab0ab67ce03dcbef51e8613c1444aec866ff9bf60f6d241 +size 41683 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png index 8ee52bca11..4cb5fc6e48 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38fa52e6e755d86aa79a029427f04146a3939ac2038cc15717f5aec84ce3be20 -size 40870 +oid sha256:c015bae5b3ce8c4447c62a13fb74d855c23b0e41bf0b128a1834d30781c0d1f8 +size 41110 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 a63b53d2c1..157e74f7f0 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:b7fdd2e68114457368c5d19fe117b2d5a86a02ca475925c9fae0269ff92f5144 -size 66312 +oid sha256:dc02a7c7b38753d9509b3cb0fb2eb0e29b6d6c79b8471ba4dc7dd5b81b13d15b +size 50892 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 new file mode 100644 index 0000000000..a63b53d2c1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7fdd2e68114457368c5d19fe117b2d5a86a02ca475925c9fae0269ff92f5144 +size 66312 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 b8d8a2cf4c..e4a14ae551 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:1cb21bd5e7d348d1d07ca4b6a360f26a4735cc9760fdd7e3b4b1bcde32da6f08 -size 62655 +oid sha256:39430896a687266d7a60890fdac5dbca6d36c87fe30d07619c9c50e23c3f77be +size 56972 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 157e74f7f0..b8d8a2cf4c 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:dc02a7c7b38753d9509b3cb0fb2eb0e29b6d6c79b8471ba4dc7dd5b81b13d15b -size 50892 +oid sha256:1cb21bd5e7d348d1d07ca4b6a360f26a4735cc9760fdd7e3b4b1bcde32da6f08 +size 62655 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 d2b5044048..74826e1e9e 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:52441a5e250b027ddd40ee32754a63c6206762084f47ecfa7e057e2ac77e78a8 -size 69120 +oid sha256:805245ffe7f4e99a0f5927b89bcaa6ab8eaf7cc603d352b14b8109e76eecbdf2 +size 51742 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 new file mode 100644 index 0000000000..d2b5044048 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52441a5e250b027ddd40ee32754a63c6206762084f47ecfa7e057e2ac77e78a8 +size 69120 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 6e371532de..1ad21c4d64 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:86273e812cbc6245527c3d1f138111ece99375aec0d68728882b656b01687bff -size 64392 +oid sha256:289819914863942caad5202131efddd9b880b4af64a36d4b45fb547b5b1ec47f +size 55916 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 74826e1e9e..6e371532de 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:805245ffe7f4e99a0f5927b89bcaa6ab8eaf7cc603d352b14b8109e76eecbdf2 -size 51742 +oid sha256:86273e812cbc6245527c3d1f138111ece99375aec0d68728882b656b01687bff +size 64392 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en.png index da80c7af53..08966f9b39 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:645a7fb89953ff6e72119a105ac966cabaf8ca6a68f3023534301e3471b942f2 -size 49262 +oid sha256:42fd1d9b089b026a9edf1a0ecb71da47a4517062cc19f61e16e70f9b3276ab30 +size 59658 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en.png index 6aa030a83a..25f2f73063 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea52fd146c53b690c1273b090c6b66701a8f40d80eb1dc693898c3dbf8b24def -size 49115 +oid sha256:314ffd2ccda8d0315a27e0cf08fa6d158ddf38fa0903e1e8d7fe45361254aa0d +size 59525 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en.png index a31686aaad..c9b289e9d9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf3e4ffa06a77fbaa9dc9985274d6b1324fb023f5eeb2b9d229af0854b41e236 -size 49095 +oid sha256:dc25ef0688d2aa9be1fcade1dfa282b243cc3c45bde01d3571b2ad9278dcda74 +size 59509 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en.png index 421367fee7..3398dc3826 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f68a09667dc19c935decb5b640bed2410dd16ae88c0d0ab6ca410225e48a87da -size 49111 +oid sha256:229b570a3c938e75f5d7b33c7fec89be9582ec06aa85dfa587b995219b145e88 +size 59517 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en.png index fbcd0538aa..6767c03276 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5defae968cccb0e78ceff23912a364cf72f9ed61ea6ae3cc9770af9c39b05f9d -size 49035 +oid sha256:9c9caa916637c212f2cf1e5d175a9cf096b83606e1161b7c2bb1c8ddcb142e48 +size 59363 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en.png index 7a3e1e9682..e9434a4041 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c590f67875f9bdc460ab82b59cd3c9e11147bfadc59028552071b64fd440f211 -size 49259 +oid sha256:296c4b7449992b4aaa478038db9b6dd4e687ca05f82cc351db258e3f178adb6f +size 59656 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en.png index 9c788eb3ab..dcbfd047c6 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d48af05c854a8f313f10aaa774e4268756dcf7e0c2bea0e138dc6cd84137673 -size 48995 +oid sha256:f13aab730039d52c1d92e5ed3ad601095cf7c8b3bd76ddf2303f2006ea6f6092 +size 59193 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en.png index 9d375b0b74..3526e1e6d7 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1507eeffcfacfcf6c84494f09d89ef3478748ae31f8fee4a9922e4188ccb454 -size 48563 +oid sha256:961959403b1763abd537e05865d86475161e650e61e51e65202522d08faad55b +size 58771 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en.png index fac0ba3da5..c1707ebef8 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4146fd85887aca1a0a65ef559976c1436d1fc2b0de522295f27bfaef1d904ea4 -size 55410 +oid sha256:2e5df1191bf18d6c922ca3d2a22584c98c4b871254512ea86cfb324fddf8a2d0 +size 62520 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png index 9feee4d98d..880c259789 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b832525da2a7744eff8165c842652bfff4742b728b000b1c8ef8be81bca75efe -size 47052 +oid sha256:ad35559b964b2d1197b256348e175a8082bffc9247024a546eea86655c9c0d8e +size 56855 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png index 9a4a40c0e9..c3e7180e3b 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ae8264008b6396f31332263472c910b492fffdb9dbf7d0186b44a272476c02c -size 46903 +oid sha256:94b94eca0502a081fbb862c426b5063275ac948c8d998e34f40e362a82591d75 +size 56707 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png index fa74fd4b96..bb6e078571 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4842f6e88df23ae0050f0052ebe9581d6c19ac5e29a0c553716012713fbb00aa -size 46901 +oid sha256:46bd8afffd3a9c4cfb92c7d9ea2c2d0a3c4942115a80809d6bbc16037e2839a1 +size 56705 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png index 5f9af21b6e..c0ea01e3b0 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7888dbb0ed5460c54796a6b2c232945bd4449e57feda4aed89ddce097a3c35b -size 46911 +oid sha256:5fc17122d1466b14e85a49165bd3b462756181ec26cbce280437f015734c8e40 +size 56716 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png index fe34c6d5c1..efc3e893a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:215745acc9dc0c86e239acf614c94f1cd46e96cf1b6dd7cdca514d7a4a82f835 -size 46742 +oid sha256:12cb6a8ee6846a0f44d5e14599fd7c6dbf97da97d35c6b6c83545d601a246efe +size 56563 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png index 9df6b272e4..7ee27433d6 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5d099940f91cb3e520f6eb6cbbc075bc57817f3e46e3f9fb169a36b408388f1 -size 47046 +oid sha256:1546f59a5be6b5f8883729c4b83005ebeb990188a86c100ba384da46b2a1da7e +size 56853 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png index f56d4e2115..aae9546e31 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce9c49a9d136d3bae5fb366fc47d3f48f8fabbe6963d0e53166dc1a5ec4cbc20 -size 46695 +oid sha256:714782c0f439f933f6e0127ccf54b6e09138fa23d44a3596017a23665131c119 +size 56498 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png index f19645feef..efadf6c0dc 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed86c0b3137dcd650d7ba65d7ec009146234b4e0b983b1ab8b56974000698201 -size 46242 +oid sha256:9a58efd0afbc84c88d8635738d3bb25826c4d25d790d4eaa1aba0e9efafc3b56 +size 56043 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png index 2b725b9e8f..1547c5289c 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbdf230e73c3fd93787030d6f5eac65b6a8131bf2eccb07fcd8474f191b87b06 -size 52563 +oid sha256:9d502f4f9ccf2d3eff614d9ed2b751a252acd05c1bf5cf4a8b11775c45725c3d +size 59427 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png index f9a16c247a..1cea623bff 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d57215dd58ace85ad235fb7f95ef592bfddd3c21afbf183bb5d31fbf772b69a -size 48900 +oid sha256:7c80e9985aec6b183e33f12ecb884753d1b8f67927d6d5713ea946ea8f5d6dd6 +size 59300 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png index e662b2dfbe..d32d440b79 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32926b81da50838d883edfd157e7d12cc3d43991556f87e61702b74e693ab6da -size 48798 +oid sha256:5dedcf20e368909de0a6cdeade437bff06b38a1f1b6aba3355968983f1387018 +size 59201 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png index 5ac95223f9..6f672c23b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0594480e59710892e480a83c1352a501504ae1bafb4c5c6b672c7dc62b3e75e1 -size 48801 +oid sha256:a9023d044daba4573bb9a3ed4da598bfb5c27081e4f7a75757a4796bdc44573a +size 59207 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png index ca2e552c10..54bd893d1f 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba828796a14ee9e82e01e98a89067649a7f3438a2fcc8e3ce5f69e12e86f86b0 -size 48777 +oid sha256:77eb9f159dbfa30e6f750500c2adf21b18cb5f1dfb7a574be68683db24803225 +size 59174 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png index 6f741da48f..e945e52dfa 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ebb88fa29993c785e6ca82543e7eeafefe365c0018a4f22852b988dc35db4a5 -size 48716 +oid sha256:3dca50c7a0f0d7ce96388a01456871decc76285384d2573161773219c79fb876 +size 59060 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png index 1a12af9468..02c571c605 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2521e5352ee6030f7a0fd8304b19f6a7a95fb6364d43f412eeef03799c8c0f76 -size 48901 +oid sha256:fe4181c3e521f1f788dd06e3cfb1ca078655852415dfd4f26f5480dd47bf084c +size 59303 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png index 962991ff8f..7da66a622f 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dfcf074e7fa4813218f4bf9e472f706c5581e6fc5c1f7ea1eb9ec422172190f -size 48713 +oid sha256:18602ce14232fd438450782a564354c38ebffb0c56d0c3887d88c47cae53dacf +size 58962 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png index 8917220310..015ecaf7fd 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3195a1871862ce32cdcf068608a565c56e794ff66f14473d5f6355918db79b6 -size 48402 +oid sha256:c3ae0af5031f7f850c40fa6560aa6d70127e006931a3e18cbee04acd7c7aed57 +size 58526 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png index 5fc76943b5..19d1ae2ca8 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f71219202890afcd4f43b5f57cb4d555b723df48f71848b64c8db415067d1d4b -size 55044 +oid sha256:a980564b6406bbe45b9a170a5744e45c9e019556587d433e5fdffd8ab6f1c6fd +size 62034 From f05ceb9f5dcfa3f396fe737f72922121cceb7967 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 10:47:17 +0200 Subject: [PATCH 130/471] review: Use @stringRes annotation for Int --- .../impl/timeline/components/TimelineItemCallNotifyView.kt | 2 ++ 1 file changed, 2 insertions(+) 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 f4d0d97495..0c3e548f4b 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 @@ -8,6 +8,7 @@ package io.element.android.features.messages.impl.timeline.components +import androidx.annotation.StringRes import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement @@ -91,6 +92,7 @@ internal fun TimelineItemCallNotifyView( } } +@StringRes private fun getTextRes( timelineRoomInfo: TimelineRoomInfo, content: TimelineItemRtcNotificationContent From 885d0f2cb926532e4485fd9f7d83cdbeccd5e5cf Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 10:47:32 +0200 Subject: [PATCH 131/471] review: better comment --- .../impl/timeline/components/TimelineItemCallNotifyView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0c3e548f4b..0c9c4e68c0 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 @@ -104,7 +104,7 @@ private fun getTextRes( RtcNotificationState.Started -> CommonStrings.common_call_started } } else { - // Only show declined info in DMs + // In Rooms, do not show declined info. CommonStrings.common_call_started } From 4437d4d9df8e63f8914c1a2be94e81f9d766c59f Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 10:47:46 +0200 Subject: [PATCH 132/471] review: Invert if for better readability --- .../timeline/factories/event/TimelineItemContentFactory.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index 2d884d75fd..b3409aa567 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -106,10 +106,10 @@ class TimelineItemContentFactory( is UnableToDecryptContent -> utdFactory.create(itemContent) is CallNotifyContent -> TimelineItemRtcNotificationContent( callIntent = itemContent.callIntent, - state = if (itemContent.declinedBy.isNotEmpty()) { - RtcNotificationState.Declined(itemContent.declinedBy.any { it == sessionId }) - } else { + state = if (itemContent.declinedBy.isEmpty()) { RtcNotificationState.Started + } else { + RtcNotificationState.Declined(itemContent.declinedBy.any { it == sessionId }) } ) is UnknownContent -> TimelineItemUnknownContent From 9a8046c02d1ada564dc129d5cabf09d238734bf2 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 10:48:50 +0200 Subject: [PATCH 133/471] review: Update signature and keep formatters grouped together --- .../eventformatter/impl/DefaultRoomLatestEventFormatter.kt | 2 +- .../impl/DefaultRoomLatestEventFormatterTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index eac0e9e88d..d234e7b239 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -54,8 +54,8 @@ class DefaultRoomLatestEventFormatter( private val roomMembershipContentFormatter: RoomMembershipContentFormatter, private val profileChangeContentFormatter: ProfileChangeContentFormatter, private val stateContentFormatter: StateContentFormatter, - private val permalinkParser: PermalinkParser, private val rtcNotificationContentFormatter: RtcNotificationContentFormatter, + private val permalinkParser: PermalinkParser, ) : RoomLatestEventFormatter { override fun format( latestEvent: LatestEventValue.Local, diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index 177cc27df2..e0613ed008 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -74,8 +74,8 @@ class DefaultRoomLatestEventFormatterTest { roomMembershipContentFormatter = RoomMembershipContentFormatter(fakeMatrixClient, stringProvider), profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider), stateContentFormatter = StateContentFormatter(stringProvider), - permalinkParser = FakePermalinkParser(), - rtcNotificationContentFormatter = RtcNotificationContentFormatter(fakeMatrixClient, stringProvider) + rtcNotificationContentFormatter = RtcNotificationContentFormatter(fakeMatrixClient, stringProvider), + permalinkParser = FakePermalinkParser() ) } From 9ce825308677a5f58be978e887f06aceeb5c3b61 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 11:16:45 +0200 Subject: [PATCH 134/471] review: Add unit test for notificationFormater --- .../RtcNotificationContentFormatterTest.kt | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt new file mode 100644 index 0000000000..dca34db3cc --- /dev/null +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/RtcNotificationContentFormatterTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.eventformatter.impl + +import android.content.Context +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.notification.CallIntent +import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID_3 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.services.toolbox.impl.strings.AndroidStringProvider +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import kotlin.toString + +@Suppress("LargeClass") +@RunWith(RobolectricTestRunner::class) +class RtcNotificationContentFormatterTest { + private lateinit var context: Context + private lateinit var fakeMatrixClient: FakeMatrixClient + private lateinit var formatter: RtcNotificationContentFormatter + + @Before + fun setup() { + context = RuntimeEnvironment.getApplication() as Context + fakeMatrixClient = FakeMatrixClient() + val stringProvider = AndroidStringProvider(context.resources) + formatter = RtcNotificationContentFormatter( + fakeMatrixClient, + stringProvider + ) + } + + @Test + @Config(qualifiers = "en") + fun `Should not display declined info in rooms`() { + val result = formatter.format( + CallNotifyContent( + CallIntent.VIDEO, + declinedBy = listOf(A_USER_ID_2, A_USER_ID_3) + ), + false + ) + val expected = "Call started" + assertThat(result.toString()).isEqualTo(expected) + } + + @Test + @Config(qualifiers = "en") + fun `Declined by me variant`() { + val result = formatter.format( + CallNotifyContent( + CallIntent.VIDEO, + declinedBy = listOf(fakeMatrixClient.sessionId) + ), + true + ) + val expected = "You declined a call" + assertThat(result.toString()).isEqualTo(expected) + } + + @Test + @Config(qualifiers = "en") + fun `Declined by other variant`() { + val result = formatter.format( + CallNotifyContent( + CallIntent.VIDEO, + declinedBy = listOf(A_USER_ID_2) + ), + true + ) + val expected = "Call declined" + assertThat(result.toString()).isEqualTo(expected) + } + + @Test + @Config(qualifiers = "en") + fun `Call started in DM`() { + val result = formatter.format( + CallNotifyContent( + CallIntent.AUDIO, + declinedBy = listOf() + ), + true + ) + val expected = "Call started" + assertThat(result.toString()).isEqualTo(expected) + } +} From f0f5e8be25e642d44c6f470158a40ae4e3f3f28e Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 11:20:10 +0200 Subject: [PATCH 135/471] review: Refactor preview, show all variants --- .../components/TimelineItemCallNotifyView.kt | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) 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 0c9c4e68c0..61273ff07a 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 @@ -125,21 +125,23 @@ private fun getIcon( @PreviewsDayNight @Composable internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { - Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { - listOf( - aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Started), - aTimelineRoomInfo() to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Started), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.AUDIO, RtcNotificationState.Declined(false)), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined(false)), - aTimelineRoomInfo(isDm = true) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Declined(true)), - aTimelineRoomInfo(isDm = false) to TimelineItemRtcNotificationContent(CallIntent.VIDEO, RtcNotificationState.Started), - ).forEach { (info, content) -> - TimelineItemCallNotifyView( - timelineRoomInfo = info, - event = aTimelineItemEvent(content = content), - content = content, - onLongClick = {}, - ) + Column(modifier = Modifier.padding(2.dp), verticalArrangement = Arrangement.spacedBy(2.dp)) { + listOf(false, true).forEach { isDm -> + listOf(CallIntent.AUDIO, CallIntent.VIDEO).forEach { callIntent -> + listOf( + RtcNotificationState.Started, + RtcNotificationState.Declined(byMe = false), + RtcNotificationState.Declined(byMe = true), + ).forEach { state -> + val content = TimelineItemRtcNotificationContent(callIntent, state) + TimelineItemCallNotifyView( + timelineRoomInfo = aTimelineRoomInfo(isDm = isDm), + event = aTimelineItemEvent(content = content), + content = content, + onLongClick = {}, + ) + } + } } } } From c5a054b9e3d48b1df20b0f25735b7e19f0eb3de7 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 11 May 2026 09:36:54 +0000 Subject: [PATCH 136/471] Update screenshots --- ...imeline.components_TimelineItemCallNotifyView_Day_0_en.png | 4 ++-- ...eline.components_TimelineItemCallNotifyView_Night_0_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 13b76dc106..e89697a8cc 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:0a618360cff745818c0ad066b4c6599ff804d9c022e6b54dfec97aa152a84f29 -size 27716 +oid sha256:5529e89e00208e38522f5206f5b8d304bd472b27071cab4e0d3c2daf3cb64db0 +size 49741 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 4d1d5d8f00..37037907c8 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:54e56f77c535db49bb9352ca09033fc81ba21b71e7c28079aec8f9bc09dcc1ad -size 26644 +oid sha256:fa29aaa82f21912dd5147ffb6fdc457fd5880e8be4abde4f0177d0ab1a412fe2 +size 48245 From 90ce30d6e7f70e47a8d6690500ebe2c1f35f1f6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 14:13:23 +0200 Subject: [PATCH 137/471] Update actions/add-to-project action to v2 (#6758) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/triage-incoming.yml | 2 +- .github/workflows/triage-labelled.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml index 8e8d03c9c4..b93ea81403 100644 --- a/.github/workflows/triage-incoming.yml +++ b/.github/workflows/triage-incoming.yml @@ -10,7 +10,7 @@ jobs: triage-new-issues: runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/91 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index 3ec20f332b..0b587369ed 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -14,7 +14,7 @@ jobs: if: > github.repository == 'element-hq/element-x-android' steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/43 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} @@ -23,7 +23,7 @@ jobs: name: Move triaged needs info issues on board runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 id: addItem with: project-url: https://github.com/orgs/element-hq/projects/91 @@ -47,7 +47,7 @@ jobs: if: > contains(github.event.issue.labels.*.name, 'Team: Element X Feature') steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/73 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} @@ -58,7 +58,7 @@ jobs: if: > contains(github.event.issue.labels.*.name, 'Team: Verticals Feature') steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/57 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} @@ -70,7 +70,7 @@ jobs: contains(github.event.issue.labels.*.name, 'Team: QA') || contains(github.event.issue.labels.*.name, 'X-Needs-Signoff') steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/69 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} @@ -81,7 +81,7 @@ jobs: if: > contains(github.event.issue.labels.*.name, 'X-Needs-Signoff') steps: - - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + - uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2 with: project-url: https://github.com/orgs/element-hq/projects/89 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} From 7f545079ad4ebb187bfdaa2b042ffe4614017883 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 11 May 2026 14:49:53 +0200 Subject: [PATCH 138/471] Prevent user from starting LLS in thread --- .../impl/share/ShareLocationPresenter.kt | 7 ++- .../impl/share/ShareLocationPresenterTest.kt | 44 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index b56f9c7cb3..6c0d2120bc 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -171,7 +171,7 @@ class ShareLocationPresenter( dialogState = dialogState, trackUserLocation = trackUserPosition, hasLocationPermission = permissionsState.isAnyGranted, - canShareLiveLocation = isLiveLocationSharingEnabled, + canShareLiveLocation = isLiveLocationSharingEnabled && timelineMode.canShareLiveLocation(), appName = appName, startLiveLocationAction = startLiveLocationAction.value, eventSink = ::handleEvent, @@ -210,4 +210,9 @@ class ShareLocationPresenter( } } +private fun Timeline.Mode.canShareLiveLocation() = when (this) { + is Timeline.Mode.Thread -> false + else -> true +} + private fun generateBody(uri: String): String = "Location was shared at $uri" diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index 855deba694..dff5f05ee9 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -29,6 +29,7 @@ import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.dateformatter.test.FakeDurationFormatter +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -40,6 +41,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -82,13 +84,14 @@ class ShareLocationPresenterTest { private fun TestScope.createShareLocationPresenter( joinedRoom: JoinedRoom = FakeJoinedRoom(), + timelineMode: Timeline.Mode = Timeline.Mode.Live, locationActions: FakeLocationActions = fakeLocationActions, liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), ): ShareLocationPresenter = ShareLocationPresenter( permissionsPresenterFactory = { fakePermissionsPresenter }, room = joinedRoom, - timelineMode = Timeline.Mode.Live, + timelineMode = timelineMode, analyticsService = fakeAnalyticsService, messageComposerContext = fakeMessageComposerContext, locationActions = locationActions, @@ -653,6 +656,45 @@ class ShareLocationPresenterTest { cancelAndIgnoreRemainingEvents() } } + + @Test + fun `canShareLiveLocation is false when the feature is disabled`() = runTest { + fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, false) + val shareLocationPresenter = createShareLocationPresenter( + timelineMode = Timeline.Mode.Live, + ) + shareLocationPresenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.canShareLiveLocation).isFalse() + } + } + + @Test + fun `canShareLiveLocation is true when the feature is enabled`() = runTest { + fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) + val shareLocationPresenter = createShareLocationPresenter( + timelineMode = Timeline.Mode.Live, + ) + shareLocationPresenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.canShareLiveLocation).isTrue() + } + } + + @Test + fun `canShareLiveLocation is false in thread timeline`() = runTest { + fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) + val shareLocationPresenter = createShareLocationPresenter( + timelineMode = Timeline.Mode.Thread(A_THREAD_ID), + ) + shareLocationPresenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.canShareLiveLocation).isFalse() + } + } } private fun createLiveLocationStore( From 6bc8cd84e4ce625a5e2a484393cb84970dab04e0 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 16:54:30 +0200 Subject: [PATCH 139/471] fixup test compilation --- .../mediaviewer/impl/datasource/DefaultEventItemFactoryTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/DefaultEventItemFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/DefaultEventItemFactoryTest.kt index 6602f475eb..ab3ffa98c7 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/DefaultEventItemFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/DefaultEventItemFactoryTest.kt @@ -62,7 +62,7 @@ class DefaultEventItemFactoryTest { fun `create check all null cases`() { val factory = createEventItemFactory() val contents = listOf( - CallNotifyContent(callIntent = CallIntent.VIDEO), + CallNotifyContent(callIntent = CallIntent.VIDEO, emptyList()), FailedToParseMessageLikeContent("", ""), FailedToParseStateContent("", "", ""), LegacyCallInviteContent, From 1ffc09e046ecb24023ef9aac5a06d82446c0cbfa Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 11 May 2026 16:55:40 +0200 Subject: [PATCH 140/471] Stop removing the `logs` dir when clearing cache (#6765) --- .../features/preferences/impl/tasks/ClearCacheUseCase.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt index 141adafe2b..de6ccf5da7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/tasks/ClearCacheUseCase.kt @@ -55,7 +55,12 @@ class DefaultClearCacheUseCase( // Clear OkHttp cache okHttpClient().cache?.delete() // Clear app cache - context.cacheDir.deleteRecursively() + context.cacheDir?.listFiles { + // But keep the logs + it.name != "logs" + }?.onEach { + it.deleteRecursively() + } // Clear some settings seenInvitesStore.clear() // Ensure any error will be displayed again From 5e5e0bbc6e555437b62ec70e80ac7cde433ba088 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 17:00:49 +0200 Subject: [PATCH 141/471] Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.0 (#6759) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4a1e0c2f80..7a79320530 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -168,7 +168,7 @@ test_truth = "com.google.truth:truth:1.4.5" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.22" test_robolectric = "org.robolectric:robolectric:4.16.1" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } -test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.8.2" +test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.0" test_detekt_api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" } test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" } From 11476c73cfe71b3e9d137bb4dd3bfdc533010c88 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 11 May 2026 17:22:16 +0200 Subject: [PATCH 142/471] Adapt to new DM definition changes in the SDK (#6748) * Set `DmRoomDefinition.TwoPeople` in `ClientBuilder`. This applies the 'direct and with at most 2 non-service members' rule to what the SDK should consider a DM. * Map `RoomInfo.isDm` from the SDK * Map `NotificationData.isDm` from `NotificationInfo.roomInfo.isDm` * Remove `RoomIsDmCheck` file as its extension functions are now redundant. Move `Room.isDm` helper function to `BaseRoom`. * Map `isDm` in `SpaceRoom` from the SDK too * Replace `isDirect` with `isDm` where possible * Map `RoomMember.isServiceMember` from the SDK and use it to tell apart normal members of a room from service members (i.e. `RoomMembersState.getDirectRoomMember`) --- .../impl/utils/DefaultCallWidgetProvider.kt | 1 - .../datasource/RoomListRoomSummaryFactory.kt | 1 - .../android/features/invite/api/InviteData.kt | 1 - .../impl/DefaultInvitePeoplePresenterTest.kt | 6 +-- .../joinroom/impl/JoinRoomPresenter.kt | 1 - .../leaveroom/impl/LeaveRoomPresenter.kt | 1 - .../impl/LeaveBaseRoomPresenterTest.kt | 2 +- .../messages/impl/MessagesPresenter.kt | 1 - .../MessageComposerPresenter.kt | 1 - .../suggestions/SuggestionsPickerView.kt | 1 + .../list/PinnedMessagesListPresenter.kt | 1 - .../impl/timeline/TimelinePresenter.kt | 1 - .../messages/impl/MessagesPresenterTest.kt | 4 +- .../MessageComposerPresenterTest.kt | 2 +- .../NotificationSettingsPresenter.kt | 4 +- .../EditDefaultNotificationSettingNode.kt | 4 +- ...EditDefaultNotificationSettingPresenter.kt | 14 ++--- ...efaultNotificationSettingsPresenterTest.kt | 2 +- .../NotificationSettingsPresenterTest.kt | 8 +-- .../roomcall/impl/RoomCallStatePresenter.kt | 1 - .../impl/RoomCallStatePresenterTest.kt | 4 +- .../roomdetails/impl/RoomDetailsPresenter.kt | 6 +-- .../impl/RoomDetailsStateProvider.kt | 4 +- .../members/RoomMemberListStateProvider.kt | 2 + .../RoomNotificationSettingsPresenter.kt | 4 +- .../impl/RoomDetailsPresenterTest.kt | 7 ++- .../addroom/AddRoomToSpaceSearchDataSource.kt | 1 - .../space/impl/leave/LeaveSpacePresenter.kt | 2 +- .../space/impl/root/SpaceStateProvider.kt | 1 + .../impl/leave/LeaveSpacePresenterTest.kt | 6 +-- .../matrix/api/analytics/ViewRoomExt.kt | 2 +- .../NotificationSettingsService.kt | 2 +- .../libraries/matrix/api/room/BaseRoom.kt | 9 ++-- .../libraries/matrix/api/room/RoomInfo.kt | 1 + .../matrix/api/room/RoomIsDmCheck.kt | 32 ----------- .../libraries/matrix/api/room/RoomMember.kt | 1 + .../matrix/api/room/RoomMembersState.kt | 2 +- .../api/room/recent/RecentDirectRoom.kt | 1 - .../matrix/api/roomlist/RoomSummary.kt | 2 +- .../libraries/matrix/api/spaces/SpaceRoom.kt | 1 + .../matrix/api/room/RoomIsDmCheckTest.kt | 54 ------------------- .../matrix/impl/RustMatrixClientFactory.kt | 2 + .../matrix/impl/analytics/JoinedRoomExt.kt | 1 - .../impl/notification/NotificationMapper.kt | 9 +--- .../RustNotificationSettingsService.kt | 4 +- .../matrix/impl/room/JoinedRustRoom.kt | 2 +- .../libraries/matrix/impl/room/RoomInfoExt.kt | 2 +- .../matrix/impl/room/RoomInfoMapper.kt | 1 + .../matrix/impl/room/RustBaseRoom.kt | 3 +- .../impl/room/member/RoomMemberMapper.kt | 3 +- .../matrix/impl/spaces/SpaceRoomMapper.kt | 1 + .../matrix/impl/timeline/RustTimeline.kt | 1 - .../matrix/impl/analytics/JoinedExtKtTest.kt | 16 +++++- .../impl/fixtures/factories/SpaceRoom.kt | 2 +- .../fixtures/fakes/FakeFfiClientBuilder.kt | 2 + .../matrix/impl/room/RoomInfoExtTest.kt | 19 ++----- .../matrix/impl/room/RoomInfoMapperTest.kt | 4 ++ .../FakeNotificationSettingsService.kt | 4 +- .../matrix/test/room/FakeJoinedRoom.kt | 6 ++- .../matrix/test/room/RoomInfoFixture.kt | 2 + .../matrix/test/room/RoomMemberFixture.kt | 2 + .../matrix/test/room/RoomSummaryFixture.kt | 1 + .../matrix/ui/room/RoomMembersTest.kt | 52 +++--------------- .../previewutils/room/RoomMemberFixture.kt | 2 + .../previewutils/room/SpaceRoomFixture.kt | 4 +- .../NotificationBroadcastReceiverHandler.kt | 1 - 66 files changed, 115 insertions(+), 232 deletions(-) delete mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt delete mode 100644 libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt index b31b6152d0..8de7b81d6d 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt @@ -14,7 +14,6 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.widget.CallWidgetSettingsProvider import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.services.appnavstate.api.ActiveRoomsHolder diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt index 26054d7e56..e34f2845da 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListRoomSummaryFactory.kt @@ -19,7 +19,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter import io.element.android.libraries.matrix.api.room.CallIntentConsensus import io.element.android.libraries.matrix.api.room.CurrentUserMembership -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.roomlist.LatestEventValue import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.ui.model.getAvatarData diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt index 696e02a0d7..8bfea2c12c 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/InviteData.kt @@ -11,7 +11,6 @@ package io.element.android.features.invite.api import android.os.Parcelable import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomInfo -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo import io.element.android.libraries.matrix.api.spaces.SpaceRoom import kotlinx.parcelize.Parcelize diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index 4ecd74a42f..5d5a533bb6 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -540,7 +540,7 @@ internal class DefaultInvitePeoplePresenterTest { } @Test - fun `present - suggestions are loaded from recent direct rooms`() = runTest { + fun `present - suggestions are loaded from recent DM rooms`() = runTest { val dmRoomId = RoomId("!dm_room:server.org") val otherUserId = UserId("@frank:server.org") val matrixClient = FakeMatrixClient(sessionId = A_USER_ID).apply { @@ -554,7 +554,7 @@ internal class DefaultInvitePeoplePresenterTest { roomId = dmRoomId, initialRoomInfo = aRoomInfo( id = dmRoomId, - isDirect = true, + isDm = true, activeMembersCount = 2, currentUserMembership = CurrentUserMembership.JOINED, ), @@ -591,7 +591,7 @@ internal class DefaultInvitePeoplePresenterTest { roomId = dmRoomId, initialRoomInfo = aRoomInfo( id = dmRoomId, - isDirect = true, + isDm = true, activeMembersCount = 2, currentUserMembership = CurrentUserMembership.JOINED, ), diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index 1e685d3f5a..d257952209 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -44,7 +44,6 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipDetails import io.element.android.libraries.matrix.api.room.RoomType -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo diff --git a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt index 6455b45659..d11dc7e7e8 100644 --- a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt +++ b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt @@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.RoomMember -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.usersWithRole import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService import kotlinx.coroutines.CoroutineScope diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt index 59d2c1ce23..90b7f2369f 100644 --- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt +++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveBaseRoomPresenterTest.kt @@ -115,7 +115,7 @@ class LeaveBaseRoomPresenterTest { givenGetRoomResult( roomId = A_ROOM_ID, result = FakeBaseRoom().apply { - givenRoomInfo(aRoomInfo(isDirect = true, activeMembersCount = 2)) + givenRoomInfo(aRoomInfo(isDm = true, activeMembersCount = 2)) }, ) } 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 1d6f1d3c1b..6754d703a3 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 @@ -79,7 +79,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId 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 90b91691a9..d93fe0ee86 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 @@ -57,7 +57,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.room.getDirectRoomMember -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.use import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt index 3d18db12d9..ef2362c794 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/suggestions/SuggestionsPickerView.kt @@ -190,6 +190,7 @@ internal fun SuggestionsPickerViewPreview() { isIgnored = false, role = RoomMember.Role.User, membershipChangeReason = null, + isServiceMember = false, ) val anAlias = remember { RoomAlias("#room:domain.org") } SuggestionsPickerView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 6cd037484d..9e5f5ba304 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -44,7 +44,6 @@ import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.ui.strings.CommonStrings diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index faa4aabe8e..0b99c45e06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -49,7 +49,6 @@ 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.core.asEventId import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.ReceiptType 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 0c836e6cc0..65aa1d857e 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 @@ -598,7 +598,7 @@ class MessagesPresenterTest { baseRoom = FakeBaseRoom( roomPermissions = roomPermissions(), ).apply { - givenRoomInfo(aRoomInfo(isDirect = true, joinedMembersCount = 1, activeMembersCount = 1)) + givenRoomInfo(aRoomInfo(isDm = true, joinedMembersCount = 1, activeMembersCount = 1)) }, typingNoticeResult = { Result.success(Unit) }, ) @@ -1112,7 +1112,7 @@ class MessagesPresenterTest { canRedactOwn = true, canPinUnpin = true, ), - initialRoomInfo = aRoomInfo(isDirect = true, isEncrypted = true) + initialRoomInfo = aRoomInfo(isDm = true, isEncrypted = true) ).apply { givenRoomMembersState(RoomMembersState.Ready(persistentListOf(aRoomMember(userId = A_SESSION_ID), aRoomMember(userId = A_USER_ID_2)))) }, 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 7a2cc1110a..e8d106a80f 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 @@ -1066,7 +1066,7 @@ class MessageComposerPresenterTest { ) givenRoomInfo( aRoomInfo( - isDirect = true, + isDm = true, activeMembersCount = 2, ) ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 9d9e80b3f3..12f13e734b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -223,7 +223,7 @@ class NotificationSettingsPresenter( notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = encryptedGroupDefaultMode != RoomNotificationMode.ALL_MESSAGES, mode = RoomNotificationMode.ALL_MESSAGES, - isOneToOne = false, + isDM = false, ) } @@ -234,7 +234,7 @@ class NotificationSettingsPresenter( notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = encryptedOneToOneDefaultMode != RoomNotificationMode.ALL_MESSAGES, mode = RoomNotificationMode.ALL_MESSAGES, - isOneToOne = true, + isDM = true, ) } }.fold( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt index 96097983c9..1357caeef3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt @@ -34,12 +34,12 @@ class EditDefaultNotificationSettingNode( } data class Inputs( - val isOneToOne: Boolean + val isDm: Boolean ) : NodeInputs private val callback: Callback = callback() private val inputs = inputs() - private val presenter = presenterFactory.create(inputs.isOneToOne) + private val presenter = presenterFactory.create(inputs.isDm) @Composable override fun View(modifier: Modifier) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index 178f7033f3..f2cb8b02cc 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -42,12 +42,12 @@ import kotlin.time.Duration.Companion.seconds @AssistedInject class EditDefaultNotificationSettingPresenter( private val notificationSettingsService: NotificationSettingsService, - @Assisted private val isOneToOne: Boolean, + @Assisted private val isDm: Boolean, private val roomListService: RoomListService, ) : Presenter { @AssistedFactory interface Factory { - fun create(isOneToOne: Boolean): EditDefaultNotificationSettingPresenter + fun create(isDm: Boolean): EditDefaultNotificationSettingPresenter } private val collator = Collator.getInstance().apply { @@ -86,7 +86,7 @@ class EditDefaultNotificationSettingPresenter( } return EditDefaultNotificationSettingState( - isOneToOne = isOneToOne, + isOneToOne = isDm, mode = mode.value, roomsWithUserDefinedMode = roomsWithUserDefinedMode.value.toImmutableList(), changeNotificationSettingAction = changeNotificationSettingAction.value, @@ -96,7 +96,7 @@ class EditDefaultNotificationSettingPresenter( } private fun CoroutineScope.fetchSettings(mode: MutableState) = launch { - mode.value = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = isOneToOne).getOrThrow() + mode.value = notificationSettingsService.getDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = isDm).getOrThrow() } @OptIn(FlowPreview::class) @@ -129,7 +129,7 @@ class EditDefaultNotificationSettingPresenter( val roomWithUserDefinedRules: Set = notificationSettingsService.getRoomsWithUserDefinedRules().getOrDefault(emptyList()).toSet() roomsWithUserDefinedMode.value = summaries .filter { roomSummary -> - roomWithUserDefinedRules.contains(roomSummary.roomId) && roomSummary.isOneToOne == isOneToOne + roomWithUserDefinedRules.contains(roomSummary.roomId) && roomSummary.isDm == isDm } .map { roomSummary -> EditNotificationSettingRoomInfo( @@ -154,9 +154,9 @@ class EditDefaultNotificationSettingPresenter( private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState>) = launch { action.runUpdatingStateNoSuccess { // On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did). - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne) + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isDM = isDm) .map { - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne) + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isDM = isDm) } } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt index e03b65f0a9..509d00db25 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/EditDefaultNotificationSettingsPresenterTest.kt @@ -198,7 +198,7 @@ class EditDefaultNotificationSettingsPresenterTest { ): EditDefaultNotificationSettingPresenter { return EditDefaultNotificationSettingPresenter( notificationSettingsService = notificationSettingsService, - isOneToOne = false, + isDm = false, roomListService = roomListService, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt index 9b36c477a4..26bb0ba1ee 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt @@ -61,8 +61,8 @@ class NotificationSettingsPresenterTest { val notificationSettingsService = FakeNotificationSettingsService() val presenter = createNotificationSettingsPresenter(notificationSettingsService) presenter.test { - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES) + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isDM = false, mode = RoomNotificationMode.ALL_MESSAGES) + notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isDM = false, mode = RoomNotificationMode.ALL_MESSAGES) val updatedState = consumeItemsUntilPredicate { (it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid) ?.defaultGroupNotificationMode == RoomNotificationMode.ALL_MESSAGES @@ -79,12 +79,12 @@ class NotificationSettingsPresenterTest { presenter.test { notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = true, - isOneToOne = false, + isDM = false, mode = RoomNotificationMode.ALL_MESSAGES ) notificationSettingsService.setDefaultRoomNotificationMode( isEncrypted = false, - isOneToOne = false, + isDM = false, mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY ) val updatedState = consumeItemsUntilPredicate { 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 index 3d863321e3..15e320c8f8 100644 --- 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 @@ -23,7 +23,6 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.notification.CallIntent import io.element.android.libraries.matrix.api.room.CallIntentConsensus import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canCall import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState 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 index 4c6fcf8e59..0612adbea1 100644 --- 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 @@ -89,10 +89,10 @@ class RoomCallStatePresenterTest { } @Test - fun `present - initial state - when is direct room`() = runTest { + fun `present - initial state - when is DM room`() = runTest { val room = FakeJoinedRoom( baseRoom = FakeBaseRoom( - initialRoomInfo = aRoomInfo(isDirect = true), + initialRoomInfo = aRoomInfo(isDm = true), roomPermissions = roomPermissions(true), ) ) 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 2ba7b4fa4a..56e5e1735e 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 @@ -42,7 +42,6 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomMember -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState @@ -53,7 +52,6 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -145,7 +143,7 @@ class RoomDetailsPresenter( } RoomDetailsEvent.UnmuteNotification -> { scope.launch(dispatchers.io) { - notificationSettingsService.unmuteRoom(room.roomId, isEncrypted, room.isOneToOne) + notificationSettingsService.unmuteRoom(room.roomId, isEncrypted, room.isDm()) } } is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite) @@ -184,7 +182,7 @@ class RoomDetailsPresenter( isFavorite = isFavorite, displayRolesAndPermissionsSettings = !isDm && permissions.canEditRolesAndPermissions, isPublic = joinRule == JoinRule.Public, - heroes = roomInfo.heroes.toImmutableList(), + heroes = roomInfo.heroes, pinnedMessagesCount = pinnedMessagesCount, snackbarMessage = snackbarMessage, canShowKnockRequests = canShowKnockRequests, 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 a8cd84be72..ad627d8677 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 @@ -74,6 +74,7 @@ fun aDmRoomMember( isIgnored: Boolean = false, role: RoomMember.Role = RoomMember.Role.User, membershipChangeReason: String? = null, + isServiceMember: Boolean = false, ) = RoomMember( userId = userId, displayName = displayName, @@ -83,7 +84,8 @@ fun aDmRoomMember( powerLevel = powerLevel, isIgnored = isIgnored, role = role, - membershipChangeReason = membershipChangeReason + membershipChangeReason = membershipChangeReason, + isServiceMember = isServiceMember, ) fun aRoomDetailsState( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt index fb213ef0cc..23c6292294 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt @@ -128,6 +128,7 @@ fun aRoomMember( isIgnored: Boolean = false, role: RoomMember.Role = RoomMember.Role.User, membershipChangeReason: String? = null, + isServiceMember: Boolean = false, ) = RoomMember( userId = userId, displayName = displayName, @@ -138,6 +139,7 @@ fun aRoomMember( isIgnored = isIgnored, role = role, membershipChangeReason = membershipChangeReason, + isServiceMember = isServiceMember, ) fun aRoomMemberList() = persistentListOf( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt index c7930b5895..08dbf2019d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsPresenter.kt @@ -160,7 +160,7 @@ class RoomNotificationSettingsPresenter( suspend { val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow() pendingModeState.value = null - notificationSettingsService.getRoomNotificationSettings(room.roomId, isEncrypted, room.isOneToOne).getOrThrow() + notificationSettingsService.getRoomNotificationSettings(room.roomId, isEncrypted, room.isDm()).getOrThrow() }.runCatchingUpdatingState(roomNotificationSettings) } @@ -170,7 +170,7 @@ class RoomNotificationSettingsPresenter( val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow() defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode( isEncrypted, - room.isOneToOne + room.isDm() ).getOrThrow() } 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 1b93a92884..3da2f5cf25 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 @@ -199,7 +199,7 @@ class RoomDetailsPresenterTest { givenRoomInfo( aRoomInfo( isEncrypted = true, - isDirect = true, + isDm = true, ) ) } @@ -284,7 +284,7 @@ class RoomDetailsPresenterTest { givenRoomInfo( aRoomInfo( isEncrypted = true, - isDirect = true, + isDm = true, ) ) } @@ -307,7 +307,6 @@ class RoomDetailsPresenterTest { val myRoomMember = aRoomMember(A_SESSION_ID) val otherRoomMember = aRoomMember(A_USER_ID_2) val room = aJoinedRoom( - isDirect = true, topic = null, roomPermissions = roomPermissions(), userDisplayNameResult = { Result.success(A_USER_NAME) }, @@ -325,7 +324,7 @@ class RoomDetailsPresenterTest { givenRoomInfo( aRoomInfo( - isDirect = true, + isDm = true, activeMembersCount = 2, topic = null, ) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceSearchDataSource.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceSearchDataSource.kt index 10c70d6f7d..882db26f00 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceSearchDataSource.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceSearchDataSource.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.recent.getRecentlyVisitedRoomInfoFlow import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt index 4e1fd34673..e0876c255c 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenter.kt @@ -67,7 +67,7 @@ class LeaveSpacePresenter( .orEmpty() .partition { it.spaceRoom.roomId == leaveSpaceHandle.id } // By default select all rooms that can be left - val otherRoomsExcludingDm = otherRooms.filter { it.spaceRoom.isDirect != true } + val otherRoomsExcludingDm = otherRooms.filter { it.spaceRoom.isDm != true } selectedRoomIds = otherRoomsExcludingDm .filter { it.isLastOwner.not() } .map { it.spaceRoom.roomId } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt index c43257b383..20f4918a98 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt @@ -109,6 +109,7 @@ private fun aSpaceInfo( avatarUrl = null, isPublic = true, isDirect = false, + isDm = false, isEncrypted = false, joinRule = joinRule, isSpace = true, diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt index b3b6fc7976..021f52defd 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/leave/LeaveSpacePresenterTest.kt @@ -98,13 +98,13 @@ class LeaveSpacePresenterTest { listOf( aLeaveSpaceRoom(spaceRoom = aSpace), aLeaveSpaceRoom( - spaceRoom = aSpaceRoom(roomId = A_ROOM_ID, isDirect = false) + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID, isDm = false) ), aLeaveSpaceRoom( - spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_2, isDirect = true) + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_2, isDm = true) ), aLeaveSpaceRoom( - spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_3, isDirect = null) + spaceRoom = aSpaceRoom(roomId = A_ROOM_ID_3, isDm = null) ), ) ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt index ac3b0c8e3a..f515ce8c32 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt @@ -19,7 +19,7 @@ fun BaseRoom.toAnalyticsViewRoom( val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home return ViewRoom( - isDM = info().isDirect, + isDM = info().isDm, isSpace = info().isSpace, trigger = trigger, activeSpace = activeSpace, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt index 4d8ce8afb4..7ae1ca4886 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notificationsettings/NotificationSettingsService.kt @@ -21,7 +21,7 @@ interface NotificationSettingsService { val notificationSettingsChangeFlow: SharedFlow suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result - suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result + suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isDM: Boolean): Result suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result suspend fun muteRoom(roomId: RoomId): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt index 589f88e9fd..f7f4924d5a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt @@ -61,13 +61,12 @@ interface BaseRoom : Closeable { */ fun info(): RoomInfo = roomInfoFlow.value - fun predecessorRoom(): PredecessorRoom? - /** - * A one-to-one is a room with exactly 2 members. - * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules). + * Returns whether the [BaseRoom] is a DM, with an updated state from the latest [RoomInfo]. */ - val isOneToOne: Boolean get() = info().activeMembersCount == 2L + fun isDm() = roomInfoFlow.value.isDm + + fun predecessorRoom(): PredecessorRoom? /** * Try to load the room members and update the membersFlow. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt index 5247e402a6..b9ed8d61b1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt @@ -29,6 +29,7 @@ data class RoomInfo( val avatarUrl: String?, val isPublic: Boolean?, val isDirect: Boolean, + val isDm: Boolean, val isEncrypted: Boolean?, val joinRule: JoinRule?, val isSpace: Boolean, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt deleted file mode 100644 index f33319e2ee..0000000000 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.api.room - -import kotlinx.coroutines.flow.first - -/** - * Returns whether the room with the provided info is a DM. - * A DM is a room with at most 2 active members (one of them may have left). - * - * @param isDirect true if the room is direct - * @param activeMembersCount the number of active members in the room (joined or invited) - */ -fun isDm(isDirect: Boolean, activeMembersCount: Int): Boolean { - return isDirect && activeMembersCount <= 2 -} - -/** - * Returns whether the [BaseRoom] is a DM, with an updated state from the latest [RoomInfo]. - */ -suspend fun BaseRoom.isDm() = roomInfoFlow.first().isDm - -/** - * Returns whether the [RoomInfo] is from a DM. - */ -val RoomInfo.isDm get() = isDm(isDirect, activeMembersCount.toInt()) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 0b3d7071c8..abf685a38b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -22,6 +22,7 @@ data class RoomMember( val isIgnored: Boolean, val role: Role, val membershipChangeReason: String?, + val isServiceMember: Boolean, ) { /** * Role of the RoomMember, based on its [powerLevel]. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembersState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembersState.kt index 1c35fab7d0..2c93fa94f0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembersState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMembersState.kt @@ -40,5 +40,5 @@ fun RoomMembersState.activeRoomMembers(): List { fun RoomMembersState.getDirectRoomMember(roomInfo: RoomInfo, sessionId: SessionId): RoomMember? { return roomMembers() ?.takeIf { roomInfo.isDm } - ?.find { it.userId != sessionId && it.membership.isActive() } + ?.find { !it.isServiceMember && it.userId != sessionId && it.membership.isActive() } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt index 6db326c854..3441a4be3a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt @@ -12,7 +12,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.CurrentUserMembership -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.coroutines.flow.Flow diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index aca093eab6..10ca376105 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -21,5 +21,5 @@ data class RoomSummary( is LatestEventValue.Remote -> latestEvent.timestamp is LatestEventValue.RoomInvite -> latestEvent.timestamp } - val isOneToOne get() = info.activeMembersCount == 2L + val isDm = info.isDm } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt index 6a72577760..e22b0a5c9c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoom.kt @@ -38,6 +38,7 @@ data class SpaceRoom( */ val via: ImmutableList, val isDirect: Boolean?, + val isDm: Boolean?, ) { val isSpace = roomType == RoomType.Space diff --git a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt b/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt deleted file mode 100644 index 1461d28fd9..0000000000 --- a/libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.api.room - -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -class RoomIsDmCheckTest { - @Test - fun `a room is a DM only if it has at most 2 members and is direct`() { - val isDirect = true - val activeMembersCount = 2 - - val isDm = isDm(isDirect, activeMembersCount) - - assertThat(isDm).isTrue() - } - - @Test - fun `a room can be a DM if it has also a single active user`() { - val isDirect = true - val activeMembersCount = 1 - - val isDm = isDm(isDirect, activeMembersCount) - - assertThat(isDm).isTrue() - } - - @Test - fun `a room is not a DM if it's not direct`() { - val isDirect = false - val activeMembersCount = 2 - - val isDm = isDm(isDirect, activeMembersCount) - - assertThat(isDm).isFalse() - } - - @Test - fun `a room is not a DM if it has more than 2 active users`() { - val isDirect = true - val activeMembersCount = 3 - - val isDm = isDm(isDirect, activeMembersCount) - - assertThat(isDm).isFalse() - } -} 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 0bf26091c2..9a573a59c1 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 @@ -41,6 +41,7 @@ import org.matrix.rustcomponents.sdk.SlidingSyncVersion import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder import org.matrix.rustcomponents.sdk.use import timber.log.Timber +import uniffi.matrix_sdk_base.DmRoomDefinition import uniffi.matrix_sdk_base.MediaRetentionPolicy import uniffi.matrix_sdk_crypto.CollectStrategy import uniffi.matrix_sdk_crypto.DecryptionSettings @@ -169,6 +170,7 @@ class RustMatrixClientFactory( ) .enableShareHistoryOnInvite(true) .threadsEnabled(featureFlagService.isFeatureEnabled(FeatureFlags.Threads), threadSubscriptions = false) + .dmRoomDefinition(DmRoomDefinition.TWO_MEMBERS) .requestConfig( RequestConfig( timeout = 30_000uL, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt index 263ce15bd3..8a0a246c22 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt @@ -11,7 +11,6 @@ package io.element.android.libraries.matrix.impl.analytics import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.RoomInfo -import io.element.android.libraries.matrix.api.room.isDm import kotlinx.coroutines.flow.first private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index f1996dd942..bfb49d6ceb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -17,7 +17,6 @@ import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationContent import io.element.android.libraries.matrix.api.notification.NotificationData -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.services.toolbox.api.systemclock.SystemClock import org.matrix.rustcomponents.sdk.NotificationEvent @@ -37,10 +36,6 @@ class NotificationMapper( ): Result { return runCatchingExceptions { notificationItem.use { item -> - val isDm = isDm( - isDirect = item.roomInfo.isDirect, - activeMembersCount = item.roomInfo.joinedMembersCount.toInt(), - ) val timestamp = item.timestamp() ?: clock.epochMillis() NotificationData( sessionId = sessionId, @@ -50,10 +45,10 @@ class NotificationMapper( senderAvatarUrl = item.senderInfo.avatarUrl, senderDisplayName = item.senderInfo.displayName, senderIsNameAmbiguous = item.senderInfo.isNameAmbiguous, - roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { isDm }, + roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { item.roomInfo.isDm }, roomDisplayName = item.roomInfo.displayName, isDirect = item.roomInfo.isDirect, - isDm = isDm, + isDm = item.roomInfo.isDm, isSpace = item.roomInfo.isSpace, isEncrypted = item.roomInfo.isEncrypted.orFalse(), isNoisy = item.isNoisy.orFalse(), diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index 7da0f14d14..6a88e5051b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -62,11 +62,11 @@ class RustNotificationSettingsService( override suspend fun setDefaultRoomNotificationMode( isEncrypted: Boolean, mode: RoomNotificationMode, - isOneToOne: Boolean + isDM: Boolean ): Result = withContext(dispatchers.io) { runCatchingExceptions { try { - notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) + notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isDM, mode.let(RoomNotificationSettingsMapper::mapMode)) } catch (exception: NotificationSettingsException.RuleNotFound) { // `setDefaultRoomNotificationMode` updates multiple rules including unstable rules (e.g. the polls push rules defined in the MSC3930) // since production home servers may not have these rules yet, we drop the RuleNotFound error diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index caeef02e8e..e75091eadc 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -348,7 +348,7 @@ class JoinedRustRoom( roomNotificationSettingsStateFlow.value = RoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings) runCatchingExceptions { val isEncrypted = roomInfoFlow.value.isEncrypted ?: getUpdatedIsEncrypted().getOrThrow() - notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow() + notificationSettingsService.getRoomNotificationSettings(roomId = roomId, isEncrypted = isEncrypted, isOneToOne = isDm()).getOrThrow() }.map { roomNotificationSettingsStateFlow.value = RoomNotificationSettingsState.Ready(it) }.onFailure { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExt.kt index 668cfc46df..4c8da1aca7 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExt.kt @@ -18,7 +18,7 @@ import org.matrix.rustcomponents.sdk.RoomInfo */ fun RoomInfo.elementHeroes(): List { return heroes - .takeIf { isDirect && activeMembersCount.toLong() == 2L } + .takeIf { isDm } ?.takeIf { it.size == 1 } ?.map { it.map() } .orEmpty() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index deca0f8ee6..0e9aadc65b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -43,6 +43,7 @@ class RoomInfoMapper { avatarUrl = it.avatarUrl, isPublic = it.isPublic, isDirect = it.isDirect, + isDm = it.isDm, isEncrypted = when (it.encryptionState) { EncryptionState.ENCRYPTED -> true EncryptionState.NOT_ENCRYPTED -> false diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt index e73bed084e..0551891a6d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt @@ -23,7 +23,6 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.draft.ComposerDraft -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom @@ -119,7 +118,7 @@ class RustBaseRoom( innerRoom.membersNoSync().use { members -> members.nextChunk(members.len()) ?.map(RoomMemberMapper::map) - ?.firstOrNull { roomMember -> roomMember.userId != sessionId && roomMember.membership.isActive() } + ?.firstOrNull { roomMember -> !roomMember.isServiceMember && roomMember.userId != sessionId && roomMember.membership.isActive() } } } else { null diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt index 447fa427a6..33ecb74ff3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt @@ -28,7 +28,8 @@ object RoomMemberMapper { powerLevel = powerLevel, isIgnored = roomMember.isIgnored, role = mapRole(roomMember.suggestedRoleForPowerLevel, powerLevel), - membershipChangeReason = roomMember.membershipChangeReason + membershipChangeReason = roomMember.membershipChangeReason, + isServiceMember = roomMember.isServiceMember, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt index f83cd648a6..cd729d0df1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomMapper.kt @@ -36,6 +36,7 @@ class SpaceRoomMapper { worldReadable = spaceRoom.worldReadable.orFalse(), via = spaceRoom.via.toImmutableList(), isDirect = spaceRoom.isDirect, + isDm = spaceRoom.isDm, ) } } 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 7b398529a0..5da4be408d 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 @@ -20,7 +20,6 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MsgType diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedExtKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedExtKtTest.kt index 68adfe00a2..3c094f3581 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedExtKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedExtKtTest.kt @@ -43,6 +43,19 @@ class JoinedExtKtTest { @Test fun `test isDirect parameter mapping`() = runTest { assertThat(aRoom(isDirect = true).toAnalyticsJoinedRoom(null)) + .isEqualTo( + JoinedRoom( + isDM = false, + isSpace = false, + roomSize = JoinedRoom.RoomSize.One, + trigger = null + ) + ) + } + + @Test + fun `test isDm parameter mapping`() = runTest { + assertThat(aRoom(isDm = true).toAnalyticsJoinedRoom(null)) .isEqualTo( JoinedRoom( isDM = true, @@ -80,12 +93,13 @@ class JoinedExtKtTest { } private fun aRoom( + isDm: Boolean = false, isDirect: Boolean = false, isSpace: Boolean = false, joinedMemberCount: Long = 0 ): FakeBaseRoom { return FakeBaseRoom().apply { - givenRoomInfo(aRoomInfo(isDirect = isDirect, isSpace = isSpace, joinedMembersCount = joinedMemberCount)) + givenRoomInfo(aRoomInfo(isDm = isDm, isDirect = isDirect, isSpace = isSpace, joinedMembersCount = joinedMemberCount)) } } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt index 50e2f6168e..2eb46eddaf 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/SpaceRoom.kt @@ -50,5 +50,5 @@ internal fun aRustSpaceRoom( childrenCount = childrenCount, state = state, heroes = heroes, - via = emptyList() + via = emptyList(), ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt index 59d82487b9..623fe9a8cf 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt @@ -17,6 +17,7 @@ import org.matrix.rustcomponents.sdk.RequestConfig import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder import org.matrix.rustcomponents.sdk.SqliteStoreBuilder import uniffi.matrix_sdk.BackupDownloadStrategy +import uniffi.matrix_sdk_base.DmRoomDefinition import uniffi.matrix_sdk_crypto.CollectStrategy import uniffi.matrix_sdk_crypto.DecryptionSettings @@ -47,5 +48,6 @@ class FakeFfiClientBuilder( override fun sqliteStore(config: SqliteStoreBuilder): ClientBuilder = this override fun inMemoryStore(): ClientBuilder = this override fun crossProcessLockConfig(crossProcessLockConfig: CrossProcessLockConfig): ClientBuilder = this + override fun dmRoomDefinition(dmRoomDefinition: DmRoomDefinition): ClientBuilder = this override suspend fun build() = buildResult() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExtTest.kt index 86a50c3926..dd91595359 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoExtTest.kt @@ -20,8 +20,7 @@ class RoomInfoExtTest { @Test fun `get non empty element Heroes`() { val result = aRustRoomInfo( - isDirect = true, - activeMembersCount = 2uL, + isDm = true, heroes = listOf(aRustRoomHero()) ).elementHeroes() assertThat(result).isEqualTo( @@ -38,8 +37,7 @@ class RoomInfoExtTest { @Test fun `too many heroes and element Heroes is empty`() { val result = aRustRoomInfo( - isDirect = true, - activeMembersCount = 2uL, + isDm = true, heroes = listOf(aRustRoomHero(), aRustRoomHero()) ).elementHeroes() assertThat(result).isEmpty() @@ -48,18 +46,7 @@ class RoomInfoExtTest { @Test fun `not direct and element Heroes is empty`() { val result = aRustRoomInfo( - isDirect = false, - activeMembersCount = 2uL, - heroes = listOf(aRustRoomHero()) - ).elementHeroes() - assertThat(result).isEmpty() - } - - @Test - fun `too many members and element Heroes is empty`() { - val result = aRustRoomInfo( - isDirect = true, - activeMembersCount = 3uL, + isDm = false, heroes = listOf(aRustRoomHero()) ).elementHeroes() assertThat(result).isEmpty() diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt index ab353bc0f5..56b480d97f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt @@ -86,6 +86,7 @@ class RoomInfoMapperTest { privilegedCreatorsRole = true, isLowPriority = true, activeRoomCallConsensusIntent = RtcCallIntentConsensus.Full(RtcCallIntent.AUDIO), + isDm = true, ) ) ).isEqualTo( @@ -136,6 +137,7 @@ class RoomInfoMapperTest { privilegedCreatorRole = true, isLowPriority = true, activeCallIntentConsensus = CallIntentConsensus.Full(CallIntent.AUDIO), + isDm = true, ) ) } @@ -181,6 +183,7 @@ class RoomInfoMapperTest { privilegedCreatorsRole = true, isLowPriority = true, activeRoomCallConsensusIntent = RtcCallIntentConsensus.None, + isDm = false, ) ) ).isEqualTo( @@ -225,6 +228,7 @@ class RoomInfoMapperTest { privilegedCreatorRole = true, isLowPriority = true, activeCallIntentConsensus = CallIntentConsensus.None, + isDm = false, ) ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt index 564cd231b2..f7970e7971 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notificationsettings/FakeNotificationSettingsService.kt @@ -70,12 +70,12 @@ class FakeNotificationSettingsService( } } - override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result { + override suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isDM: Boolean): Result { val error = setDefaultNotificationModeError if (error != null) { return Result.failure(error) } - if (isOneToOne) { + if (isDM) { if (isEncrypted) { defaultEncryptedOneToOneRoomNotificationMode = mode } else { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt index 9f7255ab18..b4425ddd4b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt @@ -134,7 +134,11 @@ class FakeJoinedRoom( } override suspend fun updateRoomNotificationSettings(): Result = simulateLongTask { - val notificationSettings = roomNotificationSettingsService.getRoomNotificationSettings(roomId, info().isEncrypted.orFalse(), isOneToOne).getOrThrow() + val notificationSettings = roomNotificationSettingsService.getRoomNotificationSettings( + roomId = roomId, + isEncrypted = info().isEncrypted.orFalse(), + isOneToOne = isDm(), + ).getOrThrow() (roomNotificationSettingsStateFlow as MutableStateFlow).value = RoomNotificationSettingsState.Ready(notificationSettings) return Result.success(Unit) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt index c7faaba627..c15330e9dc 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -71,6 +71,7 @@ fun aRoomInfo( privilegedCreatorRole: Boolean = false, isLowPriority: Boolean = false, activeCallIntentConsensus: CallIntentConsensus = CallIntentConsensus.None, + isDm: Boolean = false, ) = RoomInfo( id = id, name = name, @@ -109,4 +110,5 @@ fun aRoomInfo( privilegedCreatorRole = privilegedCreatorRole, isLowPriority = isLowPriority, activeCallIntentConsensus = activeCallIntentConsensus, + isDm = isDm, ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomMemberFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomMemberFixture.kt index f6bc0c5ec2..33a1b68ba3 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomMemberFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomMemberFixture.kt @@ -23,6 +23,7 @@ fun aRoomMember( isIgnored: Boolean = false, role: RoomMember.Role = RoomMember.Role.User, membershipChangeReason: String? = null, + isServiceMember: Boolean = false, ) = RoomMember( userId = userId, displayName = displayName, @@ -33,6 +34,7 @@ fun aRoomMember( isIgnored = isIgnored, role = role, membershipChangeReason = membershipChangeReason, + isServiceMember = isServiceMember, ) fun aRoomMemberList() = persistentListOf( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index afe4c88f5a..32635a7eea 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -120,6 +120,7 @@ fun aRoomSummary( privilegedCreatorRole = privilegedCreatorRole, isLowPriority = isLowPriority, activeCallIntentConsensus = activeCallIntentConsensus, + isDm = false, ), latestEvent = latestEvent, ) diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/RoomMembersTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/RoomMembersTest.kt index 816ac0967e..737fff34ee 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/RoomMembersTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/RoomMembersTest.kt @@ -30,13 +30,10 @@ class RoomMembersTest { private val roomMember3 = aRoomMember(A_USER_ID_3) @Test - fun `getDirectRoomMember emits other member for encrypted DM with 2 joined members`() = runTest { + fun `getDirectRoomMember emits other member for encrypted DM`() = runTest { val joinedRoom = FakeBaseRoom( sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo( - isDirect = true, - joinedMembersCount = 2, - ) + initialRoomInfo = aRoomInfo(isDm = true, isEncrypted = true) ) moleculeFlow(RecompositionMode.Immediate) { joinedRoom.getDirectRoomMember( @@ -51,7 +48,7 @@ class RoomMembersTest { fun `getDirectRoomMember emit null if the room is not a dm`() = runTest { val joinedRoom = FakeBaseRoom( sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo(isDirect = false) + initialRoomInfo = aRoomInfo(isDm = false) ) moleculeFlow(RecompositionMode.Immediate) { joinedRoom.getDirectRoomMember( @@ -66,10 +63,7 @@ class RoomMembersTest { fun `getDirectRoomMember emits other member even if the room is not encrypted`() = runTest { val joinedRoom = FakeBaseRoom( sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo( - isDirect = true, - activeMembersCount = 2, - ) + initialRoomInfo = aRoomInfo(isDm = true) ) moleculeFlow(RecompositionMode.Immediate) { joinedRoom.getDirectRoomMember( @@ -80,42 +74,11 @@ class RoomMembersTest { } } - @Test - fun `getDirectRoomMember emit null if the room has only 1 member`() = runTest { - val joinedRoom = FakeBaseRoom( - sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo(isDirect = true) - ) - moleculeFlow(RecompositionMode.Immediate) { - joinedRoom.getDirectRoomMember( - RoomMembersState.Ready(persistentListOf(roomMember1)) - ) - }.test { - assertThat(awaitItem().value).isNull() - } - } - - @Test - fun `getDirectRoomMember emit null if the room has only 3 members`() = runTest { - val joinedRoom = FakeBaseRoom( - sessionId = A_USER_ID, - ).apply { - givenRoomInfo(aRoomInfo(isDirect = true, activeMembersCount = 3L)) - } - moleculeFlow(RecompositionMode.Immediate) { - joinedRoom.getDirectRoomMember( - RoomMembersState.Ready(persistentListOf(roomMember1, roomMember2, roomMember3)) - ) - }.test { - assertThat(awaitItem().value).isNull() - } - } - @Test fun `getDirectRoomMember emit null if the other member is not active`() = runTest { val joinedRoom = FakeBaseRoom( sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo(isDirect = true), + initialRoomInfo = aRoomInfo(isDm = true), ) moleculeFlow(RecompositionMode.Immediate) { joinedRoom.getDirectRoomMember( @@ -135,10 +98,7 @@ class RoomMembersTest { fun `getDirectRoomMember emit the other member if there are 2 active members`() = runTest { val joinedRoom = FakeBaseRoom( sessionId = A_USER_ID, - initialRoomInfo = aRoomInfo( - isDirect = true, - activeMembersCount = 2, - ) + initialRoomInfo = aRoomInfo(isDm = true) ) moleculeFlow(RecompositionMode.Immediate) { joinedRoom.getDirectRoomMember( diff --git a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt index 42fc527bb2..49f77ddefb 100644 --- a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt +++ b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/RoomMemberFixture.kt @@ -33,6 +33,7 @@ fun aRoomMember( isIgnored: Boolean = false, role: RoomMember.Role = RoomMember.Role.User, membershipChangeReason: String? = null, + isServiceMember: Boolean = false, ) = RoomMember( userId = userId, displayName = displayName, @@ -43,6 +44,7 @@ fun aRoomMember( isIgnored = isIgnored, role = role, membershipChangeReason = membershipChangeReason, + isServiceMember = isServiceMember, ) fun aRoomMemberList() = persistentListOf( diff --git a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt index 68825540a4..6f86789693 100644 --- a/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt +++ b/libraries/previewutils/src/main/kotlin/io/element/android/libraries/previewutils/room/SpaceRoomFixture.kt @@ -34,6 +34,7 @@ fun aSpaceRoom( topic: String? = null, worldReadable: Boolean = false, isDirect: Boolean? = null, + isDm: Boolean? = null, via: List = emptyList(), ) = SpaceRoom( rawName = rawName, @@ -51,5 +52,6 @@ fun aSpaceRoom( topic = topic, worldReadable = worldReadable, via = via.toImmutableList(), - isDirect = isDirect + isDirect = isDirect, + isDm = isDm, ) 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 eb08a25c6a..cb9ef8c82d 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 @@ -19,7 +19,6 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.room.CreateTimelineParams import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory From a227b830beb62b517c651a6be76cb7cad412aa19 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 11 May 2026 18:08:28 +0200 Subject: [PATCH 143/471] add test to MessageSummaryFormatter --- .../DefaultMessageSummaryFormatterTest.kt | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt new file mode 100644 index 0000000000..7d0a097b3c --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.utils + +import android.content.Context +import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.api.Location +import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent.Mode +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent +import io.element.android.features.messages.impl.utils.messagesummary.DefaultMessageSummaryFormatter +import io.element.android.libraries.matrix.api.notification.CallIntent +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.timeline.aProfileDetails +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +class DefaultMessageSummaryFormatterTest { + private val formatter = DefaultMessageSummaryFormatter( + RuntimeEnvironment.getApplication() as Context + ) + + @Test + @Config(qualifiers = "en") + fun `format call notification started`() { + val expected = formatter.format(TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Started + )) + + assertThat(expected).isEqualTo("Call started") + } + + @Test + @Config(qualifiers = "en") + fun `format call notification declined by me`() { + val expected = formatter.format(TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Declined(byMe = true) + )) + + assertThat(expected).isEqualTo("You declined a call") + } + + @Test + @Config(qualifiers = "en") + fun `format call notification declined`() { + val expected = formatter.format(TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Declined(byMe = false) + )) + + assertThat(expected).isEqualTo("Call declined") + } + + @Test + @Config(qualifiers = "en") + fun `format live location`() { + val expected = formatter.format( + aLocationContent(isLive = true) + ) + + assertThat(expected).isEqualTo("Shared live location") + } + + @Test + @Config(qualifiers = "en") + fun `format static location`() { + val expected = formatter.format( + aLocationContent(isLive = false) + ) + + assertThat(expected).isEqualTo("Shared location") + } +} + +private fun aLocationContent(isLive: Boolean): TimelineItemLocationContent = TimelineItemLocationContent( + senderId = A_USER_ID, + senderProfile = aProfileDetails(), + description = null, + assetType = null, + mode = if (isLive) { + Mode.Live( + lastKnownLocation = Location.fromGeoUri("geo:1,5"), + isActive = true, + endsAt = "", + endTimestamp = 0, + isOwnUser = true + ) + } else { + Mode.Static( + location = Location.fromGeoUri("geo:1,5")!! + ) + } +) From 77b444581d9ead7cd9ef45f64c09832f9cd9d7a8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 12 May 2026 11:40:46 +0200 Subject: [PATCH 144/471] Improve `FetchPushForegroundService`'s reliability (#6757) * Improve `FetchPushForegroundService`'s reliability - Don't use DI, we can just create the notification channel. This should speed up the creation of the service and reduce the number of `ForegroundServiceDidNotStartInTimeException` received. Also use `MainScope` instead of the app's coroutine scope. - Move the wakelock releasing mechanism to `onDestroy` so it's always used. Previously, this would only happen when `stopService` was called, which would only happen when `stopSelf()` is called, but not when the OS or the service manager stops the service. * Add fallback value for the notification channel title * Replace the wrong string for the notification/channel title --------- Co-authored-by: Benoit Marty --- .../libraries/push/impl/di/PushBindings.kt | 17 ----- .../impl/push/FetchPushForegroundService.kt | 65 ++++++++++++------- .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-da/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-el/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-fi/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-hr/translations.xml | 2 +- .../src/main/res/values-hu/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 +- .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-ko/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 2 +- .../src/main/res/values-uz/translations.xml | 2 +- .../src/main/res/values-vi/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values/localazy.xml | 2 +- 22 files changed, 60 insertions(+), 62 deletions(-) delete mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/di/PushBindings.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/di/PushBindings.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/di/PushBindings.kt deleted file mode 100644 index 49b2c43bc7..0000000000 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/di/PushBindings.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.push.impl.di - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesTo -import io.element.android.libraries.push.impl.push.FetchPushForegroundService - -@ContributesTo(AppScope::class) -interface PushBindings { - fun inject(fetchPushForegroundService: FetchPushForegroundService) -} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/FetchPushForegroundService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/FetchPushForegroundService.kt index 2b3587837e..b1c90a374a 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/FetchPushForegroundService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/FetchPushForegroundService.kt @@ -13,17 +13,14 @@ import android.content.pm.ServiceInfo import android.os.Build import android.os.IBinder import android.os.PowerManager +import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import androidx.core.app.ServiceCompat -import dev.zacsweers.metro.Inject -import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.di.annotations.AppCoroutineScope -import io.element.android.libraries.push.impl.di.PushBindings -import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import timber.log.Timber @@ -34,6 +31,12 @@ private const val NOTIFICATION_ID = 1001 // This kind of foreground service can only last up to 3 minutes before onTimeout is called private val wakelockTimeout = 3.minutes.inWholeMilliseconds +// The channel ID to use for the notification of the foreground service. +private const val CHANNEL_ID = "fetch_push_notification_channel" + +// The tag to use for the wakelock, this is used for debugging purposes and should be unique to this service. +private const val WAKELOCK_TAG = "FetchPushService:WakeLock" + /** * Foreground service used to ensure the device stays awake while we handle the pushes and schedule and run the work to fetch the notification content. */ @@ -42,28 +45,35 @@ class FetchPushForegroundService : Service() { return null } - @Inject lateinit var notificationChannels: NotificationChannels - @Inject @AppCoroutineScope lateinit var coroutineScope: CoroutineScope - private val wakelock: PowerManager.WakeLock by lazy { val powerManager = getSystemService(POWER_SERVICE) as PowerManager - powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "FetchPushService:WakeLock").apply { + powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG).apply { setReferenceCounted(false) } } private var isOnForeground = false + private fun ensureNotificationChannelExists() { + NotificationManagerCompat.from(this).createNotificationChannelsCompat( + listOf( + NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW) + .setName(getString(CommonStrings.common_fetching_notifications_title_android).ifEmpty { "Syncing notifications…" }) + .setVibrationEnabled(false) + .setSound(null, null) + .build() + ) + ) + } + override fun onCreate() { - Timber.d("Creating FetchPushForegroundService") + Timber.i("Creating FetchPushForegroundService to handle incoming push, acquiring wakelock for up to $wakelockTimeout ms") + ensureNotificationChannelExists() - bindings().inject(this) - - Timber.d("Starting FetchPushForegroundService with wakelock timeout of $wakelockTimeout ms") // Start the foreground service as soon as possible - val notificationCompat = NotificationCompat.Builder(this, notificationChannels.getSilentChannelId()) + val notificationCompat = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(CommonDrawables.ic_notification) - .setContentTitle(getString(CommonStrings.common_android_fetching_notifications_title)) + .setContentTitle(getString(CommonStrings.common_fetching_notifications_title_android).ifEmpty { "Syncing notifications…" }) .setProgress(0, 0, true) .setVibrate(longArrayOf(0)) .setSound(null) @@ -103,7 +113,7 @@ class FetchPushForegroundService : Service() { // The timeout is not automatic before Android 15, so we need to schedule it ourselves if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { - coroutineScope.launch { + MainScope().launch { delay(wakelockTimeout) onTimeoutAction(calledByTheSystem = false) } @@ -112,13 +122,18 @@ class FetchPushForegroundService : Service() { return START_NOT_STICKY } - override fun stopService(intent: Intent?): Boolean { - if (isOnForeground) { - wakelock.release() - ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) - } + override fun onDestroy() { + super.onDestroy() - return super.stopService(intent) + if (isOnForeground) { + Timber.i("Destroying FetchPushForegroundService, releasing wakelock and stopping foreground") + if (wakelock.isHeld) { + wakelock.release() + } + ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) + } else { + Timber.w("Destroying FetchPushForegroundService that was not running in foreground, this is unexpected") + } } override fun onTimeout(startId: Int) { @@ -127,9 +142,9 @@ class FetchPushForegroundService : Service() { } private fun onTimeoutAction(calledByTheSystem: Boolean) { - Timber.d("onTimeoutAction, calledByTheSystem: $calledByTheSystem, isOnForeground: $isOnForeground") + Timber.w("onTimeoutAction, calledByTheSystem: $calledByTheSystem, isOnForeground: $isOnForeground") if (isOnForeground) { - Timber.d("Wakelock timeout reached, stopping FetchPushForegroundService") + Timber.w("Wakelock timeout reached, stopping FetchPushForegroundService") stopSelf() } } 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 e177c23957..4a8623f5dc 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -198,7 +198,6 @@ "Pokročilá nastavení" "obrázek" "Analytika" - "Synchronizace oznámení…" "Opustili jste místnost" "Byli jste odhlášeni z relace" "Vzhled" @@ -242,6 +241,7 @@ Důvod: %1$s." "Selhalo" "Oblíbené" "Oblíbené" + "Synchronizace oznámení…" "Soubor" "Soubor smazán" "Soubor uložen" diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index 1769a5d223..0837d0bb33 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -198,7 +198,6 @@ "Avancerede indstillinger" "et billede" "Analyse-værktøj" - "Synkroniserer notifikationer…" "Du forlod rummet" "Du blev logget ud af sessionen" "Udseende" @@ -244,6 +243,7 @@ "Mislykkedes" "Favorit" "Favoritmarkeret" + "Synkroniserer notifikationer…" "Fil" "Fil slettet" "Fil gemt" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index e6332b9df2..1a9190568c 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -192,7 +192,6 @@ "Erweiterte Einstellungen" "ein Bild" "Analysedaten" - "Benachrichtigungen werden synchronisiert…" "Du hast den Chat verlassen" "Du wurdest aus der Sitzung abgemeldet." "Erscheinungsbild" @@ -236,6 +235,7 @@ Grund: %1$s." "Fehlgeschlagen" "Favorit" "Favorisiert" + "Benachrichtigungen werden synchronisiert…" "Datei" "Datei wurde gelöscht" "Datei gespeichert" 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 a12122b00b..e5d6d0d1f5 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -192,7 +192,6 @@ "Ρυθμίσεις για προχωρημένους" "μια εικόνα" "Στατιστικά στοιχεία" - "Συγχρονισμός ειδοποιήσεων…" "Αποχωρήσατε από την αίθουσα" "Αποσυνδεθήκατε από την περίοδο λειτουργίας" "Εμφάνιση" @@ -236,6 +235,7 @@ "Απέτυχε" "Αγαπημένο" "Είναι αγαπημένο" + "Συγχρονισμός ειδοποιήσεων…" "Αρχείο" "Το αρχείο διαγράφηκε" "Το αρχείο αποθηκεύτηκε" 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 e76e3153e1..6004792f51 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -197,7 +197,6 @@ "Täiendavad seadistused" "pilt" "Analüütika" - "Sünkroonin teavitusi…" "Sina lahkusid jututoast" "Sa olid sessioonist väljaloginud" "Välimus" @@ -242,6 +241,7 @@ Põhjus: %1$s." "Ei õnnestunud" "Lemmik" "Märgitud lemmikuks" + "Sünkroonin teavitusi…" "Fail" "Fail on kustutatud" "Fail on salvestatud" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index e35ef5c2e1..1a7f6c50c9 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -193,7 +193,6 @@ "Edistyneet asetukset" "kuva" "Analytiikka" - "Synkronoidaan ilmoituksia…" "Poistuit huoneesta" "Sinut kirjattiin ulos istunnosta" "Ulkoasu" @@ -237,6 +236,7 @@ Syy: %1$s." "Epäonnistui" "Lisää suosikkeihin" "Lisätty suosikkeihin" + "Synkronoidaan ilmoituksia…" "Tiedosto" "Tiedosto poistettu" "Tiedosto tallennettu" 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 7617571f17..5cb21b772f 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -198,7 +198,6 @@ "Paramètres avancés" "une image" "Statistiques d’utilisation" - "Synchronisation des notifications…" "Vous avez quitté le salon" "Vous avez été déconnecté de la session" "Apparence" @@ -244,6 +243,7 @@ Raison : %1$s." "Échec" "Favori" "Ajouté aux favoris" + "Synchronisation des notifications…" "Fichier" "Fichier supprimé" "Fichier enregistré" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index 7c3cfbd83b..bb6cf79187 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -200,7 +200,6 @@ "Napredne postavke" "slika" "Analitika" - "Sinkronizacija obavijesti…" "Napustili ste sobu" "Odjavljeni ste iz sesije" "Izgled" @@ -246,6 +245,7 @@ Razlog: %1$s ." "Nije uspjelo" "Favorit" "Označeno kao favorit" + "Sinkronizacija obavijesti…" "Datoteka" "Datoteka je izbrisana" "Datoteka je spremljena" 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 8973ebc4f6..c477a6b6ba 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -199,7 +199,6 @@ "Speciális beállítások" "egy kép" "Elemzések" - "Értesítések szinkronizálása…" "Elhagyta a szobát" "Ki lett jelentkeztetve a munkamenetből" "Megjelenítés" @@ -245,6 +244,7 @@ Ok: %1$s." "Sikertelen" "Kedvenc" "Kedvencnek jelölve" + "Értesítések szinkronizálása…" "Fájl" "Fájl törölve" "A fájl mentve" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index ba0d6f022c..cdc523a5ab 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -199,7 +199,6 @@ "Impostazioni avanzate" "un\'immagine" "Statistiche di utilizzo" - "Sincronizzazione delle notifiche…" "Hai lasciato la stanza" "Sei stato disconnesso dalla sessione" "Aspetto" @@ -245,6 +244,7 @@ Motivo:. %1$s" "Fallito" "Preferiti" "Preferita" + "Sincronizzazione delle notifiche…" "File" "File eliminato" "File salvato" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index b7a33d4dfd..e25ca0cf4e 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -197,7 +197,6 @@ "高度な設定" "画像" "分析" - "通知を同期中…" "あなたがルームを退出" "セッションからログアウトされました" "テーマ" @@ -243,6 +242,7 @@ "失敗" "お気に入り" "お気に入り" + "通知を同期中…" "ファイル" "ファイルを削除しました" "ファイルを保存しました" diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 4f594f002b..3a029e20fc 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -190,7 +190,6 @@ "고급 설정" "이미지" "통계" - "알림 동기화 중…" "방을 떠남" "세션에서 로그아웃되었습니다." "외관" @@ -234,6 +233,7 @@ "실패" "즐겨찾기" "즐겨찾기 됨" + "알림 동기화 중…" "파일" "파일 삭제됨" "파일 저장됨" 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 30f2c383b6..1a9a45cd94 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -201,7 +201,6 @@ "Ustawienia zaawansowane" "obraz" "Dane analityczne" - "Synchronizuję powiadomienia…" "Opuściłeś pokój" "Zostałeś wylogowany z sesji" "Wygląd" @@ -247,6 +246,7 @@ Powód: %1$s." "Niepowodzenie" "Ulubione" "Ulubione" + "Synchronizuję powiadomienia…" "Plik" "Plik usunięty" "Plik zapisany" 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 a691d1d69b..fe852dcfd0 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -195,7 +195,6 @@ "Расширенные настройки" "изображение" "Аналитика" - "Синхронизация уведомлений…" "Вы покинули комнату" "Вы вышли из сессии" "Внешний вид" @@ -239,6 +238,7 @@ "Ошибка" "Избранное" "Избранное" + "Синхронизация уведомлений…" "Файл" "Файл удалён" "Файл сохранен" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index db0fa31804..a9e1d14b40 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -201,7 +201,6 @@ "Додаткові налаштування" "зображення" "Аналітика" - "Синхронізація сповіщень…" "Ви вийшли з кімнати" "Ви вийшли з сеансу" "Тема" @@ -247,6 +246,7 @@ "Помилка" "Обране" "Обране" + "Синхронізація сповіщень…" "Файл" "Файл видалено" "Файл збережено" diff --git a/libraries/ui-strings/src/main/res/values-uz/translations.xml b/libraries/ui-strings/src/main/res/values-uz/translations.xml index 304a044e0b..50d14ef4d4 100644 --- a/libraries/ui-strings/src/main/res/values-uz/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uz/translations.xml @@ -193,7 +193,6 @@ "Kengaytirilgan sozlamalar" "rasm" "Analitika" - "Bildirishnomalar sinxronlanmoqda…" "Siz xonani tark etdingiz" "Siz sessiyadan chiqdingiz" "Ko\'rinish" @@ -237,6 +236,7 @@ Sababi:%1$s." "Xatolikka uchradi" "Sevimli" "Sevimli" + "Bildirishnomalar sinxronlanmoqda…" "Fayl" "Fayl o\'chirildi" "Fayl saqlandi" diff --git a/libraries/ui-strings/src/main/res/values-vi/translations.xml b/libraries/ui-strings/src/main/res/values-vi/translations.xml index 2db0ce1dae..825ff12bea 100644 --- a/libraries/ui-strings/src/main/res/values-vi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-vi/translations.xml @@ -191,7 +191,6 @@ "Cài đặt nâng cao" "một hình ảnh" "Phân tích" - "Đang đồng bộ thông báo…" "Bạn rời phòng" "Bạn đã bị đăng xuất" "Giao diện" @@ -235,6 +234,7 @@ Lý do: %1$s ." "Thất bại" "Yêu thích" "Được yêu thích" + "Đang đồng bộ thông báo…" "Tập tin" "Tệp đã bị xóa" "Tệp đã được lưu" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 819c993bb1..b4c993e852 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -191,7 +191,6 @@ "進階設定" "影像" "分析" - "正在同步通知……" "您離開聊天室" "您已登出工作階段" "外觀" @@ -235,6 +234,7 @@ "失敗" "我的最愛" "我的最愛" + "正在同步通知……" "檔案" "檔案已刪除" "檔案已儲存" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 00bb686f78..7366a4fd95 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -196,7 +196,6 @@ "高级设置" "一张图片" "分析" - "正在同步通知…" "你离开了房间" "你已注销会话" "外观" @@ -242,6 +241,7 @@ "失败" "收藏" "已收藏" + "正在同步通知…" "文件" "文件已删除" "文件已保存" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 656ec50569..173a525919 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -199,7 +199,6 @@ "Advanced settings" "an image" "Analytics" - "Syncing notifications…" "You left the room" "You were logged out of the session" "Appearance" @@ -245,6 +244,7 @@ Reason: %1$s." "Failed" "Favourite" "Favourited" + "Syncing notifications…" "File" "File deleted" "File saved" From 72be7ff1f9a2bf1b0bff2136ab92421560185865 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 12:23:16 +0200 Subject: [PATCH 145/471] Reduce FeatureFlags `Knock` effect on room creation and room edition form. Closes #6701 --- features/knockrequests/impl/build.gradle.kts | 2 -- .../impl/data/KnockRequestsModule.kt | 7 ++----- .../impl/data/KnockRequestsService.kt | 19 ++++++------------- .../KnockRequestsBannerPresenterTest.kt | 14 -------------- .../list/KnockRequestsListPresenterTest.kt | 1 - .../roomdetails/impl/RoomDetailsPresenter.kt | 8 +------- .../impl/RoomDetailsPresenterTest.kt | 18 +----------------- 7 files changed, 10 insertions(+), 59 deletions(-) diff --git a/features/knockrequests/impl/build.gradle.kts b/features/knockrequests/impl/build.gradle.kts index 6f030479f5..e6a1a30167 100644 --- a/features/knockrequests/impl/build.gradle.kts +++ b/features/knockrequests/impl/build.gradle.kts @@ -33,9 +33,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) implementation(projects.libraries.designsystem) - implementation(projects.libraries.featureflag.api) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.featureflag.test) } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsModule.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsModule.kt index b51b78f105..73e4a2ddeb 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsModule.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsModule.kt @@ -15,8 +15,6 @@ import dev.zacsweers.metro.SingleIn import io.element.android.features.knockrequests.api.KnockRequestPermissions import io.element.android.features.knockrequests.api.knockRequestPermissions import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.powerlevels.permissionsFlow @@ -25,14 +23,13 @@ import io.element.android.libraries.matrix.api.room.powerlevels.permissionsFlow object KnockRequestsModule { @Provides @SingleIn(RoomScope::class) - fun knockRequestsService(room: JoinedRoom, featureFlagService: FeatureFlagService): KnockRequestsService { + fun knockRequestsService(room: JoinedRoom): KnockRequestsService { return KnockRequestsService( knockRequestsFlow = room.knockRequestsFlow, permissionsFlow = room.permissionsFlow(KnockRequestPermissions.DEFAULT) { perms -> perms.knockRequestPermissions() }, - isKnockFeatureEnabledFlow = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock), - coroutineScope = room.roomCoroutineScope + coroutineScope = room.roomCoroutineScope, ) } } diff --git a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsService.kt b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsService.kt index 98570e6b28..00e0a30563 100644 --- a/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsService.kt +++ b/features/knockrequests/impl/src/main/kotlin/io/element/android/features/knockrequests/impl/data/KnockRequestsService.kt @@ -12,7 +12,6 @@ import io.element.android.features.knockrequests.api.KnockRequestPermissions import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.knock.KnockRequest -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async @@ -28,26 +27,20 @@ import kotlinx.coroutines.supervisorScope class KnockRequestsService( knockRequestsFlow: Flow>, permissionsFlow: Flow, - isKnockFeatureEnabledFlow: Flow, coroutineScope: CoroutineScope, ) { // Keep track of the knock requests that have been handled, so we don't have to wait for sync to remove them. private val handledKnockRequestIds = MutableStateFlow>(emptySet()) val knockRequestsFlow = combine( - isKnockFeatureEnabledFlow, knockRequestsFlow, handledKnockRequestIds, - ) { isKnockEnabled, knockRequests, handledKnockIds -> - if (!isKnockEnabled) { - AsyncData.Success(persistentListOf()) - } else { - val presentableKnockRequests = knockRequests - .filter { it.eventId !in handledKnockIds } - .map { inner -> KnockRequestWrapper(inner) } - .toImmutableList() - AsyncData.Success(presentableKnockRequests) - } + ) { knockRequests, handledKnockIds -> + val presentableKnockRequests = knockRequests + .filter { it.eventId !in handledKnockIds } + .map { inner -> KnockRequestWrapper(inner) } + .toImmutableList() + AsyncData.Success(presentableKnockRequests) }.stateIn(coroutineScope, SharingStarted.Lazily, AsyncData.Loading()) val permissionsFlow = permissionsFlow.stateIn( diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt index 3161d3e81f..1595fcdbd9 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/banner/KnockRequestsBannerPresenterTest.kt @@ -28,18 +28,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) class KnockRequestsBannerPresenterTest { - @Test - fun `present - when feature is disabled then the banner should be hidden`() = runTest { - val knockRequests = flowOf(listOf(FakeKnockRequest())) - val presenter = createKnockRequestsBannerPresenter(isFeatureEnabled = false, knockRequestsFlow = knockRequests) - presenter.test { - skipItems(1) - awaitItem().also { state -> - assertThat(state.isVisible).isFalse() - } - } - } - @Test fun `present - when empty knock request list then the banner should be hidden`() = runTest { val knockRequests = flowOf(emptyList()) @@ -229,12 +217,10 @@ import org.junit.Test private fun TestScope.createKnockRequestsBannerPresenter( knockRequestsFlow: Flow> = flowOf(emptyList()), canAcceptKnockRequests: Boolean = true, - isFeatureEnabled: Boolean = true, ): KnockRequestsBannerPresenter { val knockRequestsService = KnockRequestsService( knockRequestsFlow = knockRequestsFlow, coroutineScope = backgroundScope, - isKnockFeatureEnabledFlow = flowOf(isFeatureEnabled), permissionsFlow = flowOf(KnockRequestPermissions(canAcceptKnockRequests, canAcceptKnockRequests, canAcceptKnockRequests)), ) return KnockRequestsBannerPresenter( diff --git a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt index 7102b01773..209e67cadf 100644 --- a/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt +++ b/features/knockrequests/impl/src/test/kotlin/io/element/android/features/knockrequests/impl/list/KnockRequestsListPresenterTest.kt @@ -298,7 +298,6 @@ internal fun TestScope.createKnockRequestsListPresenter( val knockRequestsService = KnockRequestsService( knockRequestsFlow = knockRequestsFlow, coroutineScope = backgroundScope, - isKnockFeatureEnabledFlow = flowOf(true), permissionsFlow = flowOf(KnockRequestPermissions(canAccept, canDecline, canBan)), ) return KnockRequestsListPresenter(knockRequestsService = knockRequestsService) 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 56e5e1735e..167cc2ced5 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 @@ -35,8 +35,6 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState -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.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService @@ -61,7 +59,6 @@ import kotlinx.coroutines.launch class RoomDetailsPresenter( private val client: MatrixClient, private val room: JoinedRoom, - private val featureFlagService: FeatureFlagService, private val notificationSettingsService: NotificationSettingsService, private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory, private val leaveRoomPresenter: Presenter, @@ -110,14 +107,11 @@ class RoomDetailsPresenter( } } - val isKnockRequestsEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock) - }.collectAsState(false) val knockRequestsCount by produceState(null) { room.knockRequestsFlow.collect { value = it.size } } val canShowKnockRequests by remember { - derivedStateOf { isKnockRequestsEnabled && permissions.knockRequestsPermissions.hasAny && joinRule == JoinRule.Knock } + derivedStateOf { permissions.knockRequestsPermissions.hasAny && joinRule == JoinRule.Knock } } val canShowSecurityAndPrivacy by remember { derivedStateOf { !isDm && permissions.securityAndPrivacyPermissions.hasAny(isSpace = false, joinRule = joinRule) } 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 3da2f5cf25..9c116f5a9d 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 @@ -21,9 +21,6 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers -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.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomMembersState @@ -80,11 +77,6 @@ class RoomDetailsPresenterTest { dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), analyticsService: AnalyticsService = FakeAnalyticsService(), - featureFlagService: FeatureFlagService = FakeFeatureFlagService( - mapOf( - FeatureFlags.Knock.key to false, - ) - ), encryptionService: FakeEncryptionService = FakeEncryptionService(), clipboardHelper: ClipboardHelper = FakeClipboardHelper(), appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore() @@ -106,7 +98,6 @@ class RoomDetailsPresenterTest { return RoomDetailsPresenter( client = matrixClient, room = room, - featureFlagService = featureFlagService, notificationSettingsService = matrixClient.notificationSettingsService, roomMembersDetailsPresenterFactory = roomMemberDetailsPresenterFactory, leaveRoomPresenter = { leaveRoomState }, @@ -564,17 +555,11 @@ class RoomDetailsPresenterTest { roomPermissions = roomPermissions(), joinRule = JoinRule.Knock, ) - val featureFlagService = FakeFeatureFlagService( - mapOf(FeatureFlags.Knock.key to false) - ) val presenter = createRoomDetailsPresenter( room = room, - featureFlagService = featureFlagService, ) presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) - assertThat(awaitItem().canShowKnockRequests).isFalse() - featureFlagService.setFeatureEnabled(FeatureFlags.Knock, true) assertThat(awaitItem().canShowKnockRequests).isTrue() room.givenRoomInfo(aRoomInfo(joinRule = JoinRule.Invite)) assertThat(awaitItem().canShowKnockRequests).isFalse() @@ -587,8 +572,7 @@ class RoomDetailsPresenterTest { val room = aJoinedRoom( roomPermissions = roomPermissions(), ) - val featureFlagService = FakeFeatureFlagService() - val presenter = createRoomDetailsPresenter(room = room, featureFlagService = featureFlagService) + val presenter = createRoomDetailsPresenter(room = room) presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) with(awaitItem()) { From 3234931d3c972d7751f83f5b91f70d2f78edd6f2 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 12 May 2026 15:04:13 +0200 Subject: [PATCH 146/471] Use the right analytics span as a parent in `checkNetworkConnection` (#6751) `AnalyticsLongRunningTransaction.PushToWorkManager` was incorrectly used instead of `AnalyticsLongRunningTransaction.PushToNotification`, resulting in wrongly formatter analytic traces --- .../push/impl/workmanager/FetchPendingNotificationsWorker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchPendingNotificationsWorker.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchPendingNotificationsWorker.kt index c18a0015aa..cefbd31515 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchPendingNotificationsWorker.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/workmanager/FetchPendingNotificationsWorker.kt @@ -153,7 +153,7 @@ class FetchPendingNotificationsWorker( private suspend fun checkNetworkConnection(requests: List): Result? { val networkTimeoutSpans = requests.mapNotNull { request -> - val parent = analyticsService.getLongRunningTransaction(AnalyticsLongRunningTransaction.PushToWorkManager(request.eventId)) + val parent = analyticsService.getLongRunningTransaction(AnalyticsLongRunningTransaction.PushToNotification(request.eventId)) parent?.startChild("Waiting for network connectivity", "await_network") } From c30fee7f148ffd940fe4c1d4d7125742a8f6183c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 15:21:20 +0200 Subject: [PATCH 147/471] Do not close the MediaPlayer when navigating back from a thread. --- .../features/messages/impl/threads/ThreadedMessagesNode.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 63ce48e100..be573fa92f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -67,7 +67,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsService @@ -88,7 +87,6 @@ class ThreadedMessagesNode( private val presenterFactory: MessagesPresenter.Factory, private val actionListPresenterFactory: ActionListPresenter.Factory, private val timelineItemPresenterFactories: TimelineItemPresenterFactories, - private val mediaPlayer: MediaPlayer, private val permalinkParser: PermalinkParser, private val appNavigationStateService: AppNavigationStateService, private val roomMemberModerationRenderer: RoomMemberModerationRenderer, @@ -157,9 +155,6 @@ class ThreadedMessagesNode( onStop = { appNavigationStateService.onLeavingThread(id) }, - onDestroy = { - mediaPlayer.close() - } ) } From f04c83637186198e8aa08c457bc3c9b2efaff7fb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 15:42:06 +0200 Subject: [PATCH 148/471] Update dependency io.element.android:element-call-embedded to v0.19.3 (#6766) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a79320530..8cdeecb285 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -234,7 +234,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.19.2" +element_call_embedded = "io.element.android:element-call-embedded:0.19.3" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } From d299b722e3e0514708e075e4d666d2424da493a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 15:50:47 +0200 Subject: [PATCH 149/471] Format code. --- .../DefaultMessageSummaryFormatterTest.kt | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt index 7d0a097b3c..664d21ed64 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/utils/DefaultMessageSummaryFormatterTest.kt @@ -33,33 +33,36 @@ class DefaultMessageSummaryFormatterTest { @Test @Config(qualifiers = "en") fun `format call notification started`() { - val expected = formatter.format(TimelineItemRtcNotificationContent( - callIntent = CallIntent.VIDEO, - state = RtcNotificationState.Started - )) - + val expected = formatter.format( + TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Started + ) + ) assertThat(expected).isEqualTo("Call started") } @Test @Config(qualifiers = "en") fun `format call notification declined by me`() { - val expected = formatter.format(TimelineItemRtcNotificationContent( - callIntent = CallIntent.VIDEO, - state = RtcNotificationState.Declined(byMe = true) - )) - + val expected = formatter.format( + TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Declined(byMe = true) + ) + ) assertThat(expected).isEqualTo("You declined a call") } @Test @Config(qualifiers = "en") fun `format call notification declined`() { - val expected = formatter.format(TimelineItemRtcNotificationContent( - callIntent = CallIntent.VIDEO, - state = RtcNotificationState.Declined(byMe = false) - )) - + val expected = formatter.format( + TimelineItemRtcNotificationContent( + callIntent = CallIntent.VIDEO, + state = RtcNotificationState.Declined(byMe = false) + ) + ) assertThat(expected).isEqualTo("Call declined") } @@ -69,7 +72,6 @@ class DefaultMessageSummaryFormatterTest { val expected = formatter.format( aLocationContent(isLive = true) ) - assertThat(expected).isEqualTo("Shared live location") } @@ -79,27 +81,26 @@ class DefaultMessageSummaryFormatterTest { val expected = formatter.format( aLocationContent(isLive = false) ) - assertThat(expected).isEqualTo("Shared location") } } -private fun aLocationContent(isLive: Boolean): TimelineItemLocationContent = TimelineItemLocationContent( +private fun aLocationContent(isLive: Boolean) = TimelineItemLocationContent( senderId = A_USER_ID, senderProfile = aProfileDetails(), description = null, assetType = null, mode = if (isLive) { Mode.Live( - lastKnownLocation = Location.fromGeoUri("geo:1,5"), - isActive = true, - endsAt = "", - endTimestamp = 0, - isOwnUser = true - ) + lastKnownLocation = Location.fromGeoUri("geo:1,5"), + isActive = true, + endsAt = "", + endTimestamp = 0, + isOwnUser = true, + ) } else { Mode.Static( - location = Location.fromGeoUri("geo:1,5")!! - ) + location = Location.fromGeoUri("geo:1,5")!!, + ) } ) From c68e1b8845071b6718a8ebfbc45ed5930e989b1d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 15:57:18 +0200 Subject: [PATCH 150/471] Update tspascoal/get-user-teams-membership action to v4.0.1 (#6750) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 9b4a5e3689..f0c8fd1e6f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Check membership if: github.event.pull_request.user.login != 'renovate[bot]' - uses: tspascoal/get-user-teams-membership@b1480b119326dde04ceffbeccd98e41892539c74 # v4.0.0 + uses: tspascoal/get-user-teams-membership@818140d631d5f29f26b151afbe4179f87d9ceb5e # v4.0.1 id: teams with: username: ${{ github.event.pull_request.user.login }} From ae50f6b8973f11bf51da00d0b06daf5a8a0c50bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 16:03:34 +0200 Subject: [PATCH 151/471] Update plugin sonarqube to v7.3.0.8198 (#6743) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 384b69b49f..64a7ec7031 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -268,7 +268,7 @@ paparazzi = "app.cash.paparazzi:2.0.0-alpha04" roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } -sonarqube = "org.sonarqube:7.2.3.7755" +sonarqube = "org.sonarqube:7.3.0.8198" licensee = "app.cash.licensee:1.14.1" compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } gms_google_services = { id = "com.google.gms.google-services", version = "4.4.4" } From 3f02f527f03aad526a7bf561b85fea0f7012ba9b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 16:17:10 +0200 Subject: [PATCH 152/471] Rename FF title --- .../element/android/libraries/featureflag/api/FeatureFlags.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cdda66579b..ab7dcb9960 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 @@ -102,7 +102,7 @@ enum class FeatureFlags( ), AllowBlackTheme( key = "feature.allow_black_theme", - title = "Allow black theme", + title = "Black theme", description = "Allow selecting the black appearance theme for battery saving on OLED.", defaultValue = { false }, isFinished = false, From b4305b17492fdc4153eec3c31533dc1eae90f06e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 16:56:34 +0200 Subject: [PATCH 153/471] Update plugin dependencycheck to v12.2.2 (#6760) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 64a7ec7031..97933dfb61 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -262,7 +262,7 @@ metro = { id = "dev.zacsweers.metro", version.ref = "metro" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } ktlint = "org.jlleitschuh.gradle.ktlint:14.2.0" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" -dependencycheck = "org.owasp.dependencycheck:12.2.1" +dependencycheck = "org.owasp.dependencycheck:12.2.2" dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" } paparazzi = "app.cash.paparazzi:2.0.0-alpha04" roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } From 9eac27515e844d3f1eb7fc15902534b1d739edd2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 15:35:51 +0000 Subject: [PATCH 154/471] Update dependency com.google.guava:guava to v33.6.0-jre (#6646) * Update dependency com.google.guava:guava to v33.6.0-jre * Use android version --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Benoit Marty --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97933dfb61..b5393bcc22 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -214,7 +214,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.2.0" zxing_cpp = "io.github.zxing-cpp:android:3.0.2" google_zxing = "com.google.zxing:core:3.5.4" -google_guava = "com.google.guava:guava:33.5.0-android" +google_guava = "com.google.guava:guava:33.6.0-android" haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" } haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" } From 3ca06d71ef75e4363fe8109492a231e5d6ef325d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 16:27:16 +0200 Subject: [PATCH 155/471] Rename keys on Localazy and add Black theme. --- .../preferences/impl/advanced/AdvancedSettingsState.kt | 10 +++++----- .../impl/src/main/res/values-be/translations.xml | 3 +++ .../impl/src/main/res/values-bg/translations.xml | 3 +++ .../impl/src/main/res/values-cs/translations.xml | 3 +++ .../impl/src/main/res/values-cy/translations.xml | 3 +++ .../impl/src/main/res/values-da/translations.xml | 3 +++ .../impl/src/main/res/values-de/translations.xml | 3 +++ .../impl/src/main/res/values-el/translations.xml | 3 +++ .../impl/src/main/res/values-es/translations.xml | 3 +++ .../impl/src/main/res/values-et/translations.xml | 3 +++ .../impl/src/main/res/values-eu/translations.xml | 3 +++ .../impl/src/main/res/values-fa/translations.xml | 3 +++ .../impl/src/main/res/values-fi/translations.xml | 3 +++ .../impl/src/main/res/values-fr/translations.xml | 3 +++ .../impl/src/main/res/values-hr/translations.xml | 3 +++ .../impl/src/main/res/values-hu/translations.xml | 3 +++ .../impl/src/main/res/values-in/translations.xml | 3 +++ .../impl/src/main/res/values-it/translations.xml | 3 +++ .../impl/src/main/res/values-ja/translations.xml | 3 +++ .../impl/src/main/res/values-ka/translations.xml | 3 +++ .../impl/src/main/res/values-ko/translations.xml | 3 +++ .../impl/src/main/res/values-nb/translations.xml | 3 +++ .../impl/src/main/res/values-nl/translations.xml | 3 +++ .../impl/src/main/res/values-pl/translations.xml | 3 +++ .../impl/src/main/res/values-pt-rBR/translations.xml | 3 +++ .../impl/src/main/res/values-pt/translations.xml | 3 +++ .../impl/src/main/res/values-ro/translations.xml | 3 +++ .../impl/src/main/res/values-ru/translations.xml | 3 +++ .../impl/src/main/res/values-sk/translations.xml | 3 +++ .../impl/src/main/res/values-sv/translations.xml | 3 +++ .../impl/src/main/res/values-tr/translations.xml | 3 +++ .../impl/src/main/res/values-uk/translations.xml | 3 +++ .../impl/src/main/res/values-ur/translations.xml | 3 +++ .../impl/src/main/res/values-uz/translations.xml | 3 +++ .../impl/src/main/res/values-vi/translations.xml | 3 +++ .../impl/src/main/res/values-zh-rTW/translations.xml | 3 +++ .../impl/src/main/res/values-zh/translations.xml | 3 +++ .../preferences/impl/src/main/res/values/localazy.xml | 4 ++++ .../impl/advanced/AdvancedSettingsViewTest.kt | 6 +++--- .../ui-strings/src/main/res/values-be/translations.xml | 3 --- .../ui-strings/src/main/res/values-bg/translations.xml | 3 --- .../ui-strings/src/main/res/values-cs/translations.xml | 3 --- .../ui-strings/src/main/res/values-cy/translations.xml | 3 --- .../ui-strings/src/main/res/values-da/translations.xml | 3 --- .../ui-strings/src/main/res/values-de/translations.xml | 3 --- .../ui-strings/src/main/res/values-el/translations.xml | 3 --- .../ui-strings/src/main/res/values-es/translations.xml | 3 --- .../ui-strings/src/main/res/values-et/translations.xml | 3 --- .../ui-strings/src/main/res/values-eu/translations.xml | 3 --- .../ui-strings/src/main/res/values-fa/translations.xml | 3 --- .../ui-strings/src/main/res/values-fi/translations.xml | 3 --- .../ui-strings/src/main/res/values-fr/translations.xml | 3 --- .../ui-strings/src/main/res/values-hr/translations.xml | 3 --- .../ui-strings/src/main/res/values-hu/translations.xml | 3 --- .../ui-strings/src/main/res/values-in/translations.xml | 3 --- .../ui-strings/src/main/res/values-it/translations.xml | 3 --- .../ui-strings/src/main/res/values-ja/translations.xml | 3 --- .../ui-strings/src/main/res/values-ka/translations.xml | 3 --- .../ui-strings/src/main/res/values-ko/translations.xml | 3 --- .../ui-strings/src/main/res/values-nb/translations.xml | 3 --- .../ui-strings/src/main/res/values-nl/translations.xml | 3 --- .../ui-strings/src/main/res/values-pl/translations.xml | 3 --- .../src/main/res/values-pt-rBR/translations.xml | 3 --- .../ui-strings/src/main/res/values-pt/translations.xml | 3 --- .../ui-strings/src/main/res/values-ro/translations.xml | 3 --- .../ui-strings/src/main/res/values-ru/translations.xml | 3 --- .../ui-strings/src/main/res/values-sk/translations.xml | 3 --- .../ui-strings/src/main/res/values-sv/translations.xml | 3 --- .../ui-strings/src/main/res/values-tr/translations.xml | 3 --- .../ui-strings/src/main/res/values-uk/translations.xml | 3 --- .../ui-strings/src/main/res/values-ur/translations.xml | 3 --- .../ui-strings/src/main/res/values-uz/translations.xml | 3 --- .../ui-strings/src/main/res/values-vi/translations.xml | 3 --- .../src/main/res/values-zh-rTW/translations.xml | 3 --- .../ui-strings/src/main/res/values-zh/translations.xml | 3 --- libraries/ui-strings/src/main/res/values/localazy.xml | 3 --- libraries/ui-strings/src/main/res/values/temporary.xml | 10 ---------- tools/localazy/config.json | 1 + 78 files changed, 121 insertions(+), 129 deletions(-) delete mode 100644 libraries/ui-strings/src/main/res/values/temporary.xml diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 1deb972abc..94019c1f01 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -11,9 +11,9 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource +import io.element.android.features.preferences.impl.R import io.element.android.libraries.designsystem.components.preferences.DropdownOption import io.element.android.libraries.preferences.api.store.VideoCompressionPreset -import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList data class AdvancedSettingsState( @@ -44,24 +44,24 @@ enum class ThemeOption : DropdownOption { System { @Composable @ReadOnlyComposable - override fun getText(): String = stringResource(CommonStrings.common_system) + override fun getText(): String = stringResource(R.string.theme_system) }, Light { @Composable @ReadOnlyComposable - override fun getText(): String = stringResource(CommonStrings.common_light) + override fun getText(): String = stringResource(R.string.theme_light) }, Dark { @Composable @ReadOnlyComposable - override fun getText(): String = stringResource(CommonStrings.common_dark) + override fun getText(): String = stringResource(R.string.theme_dark) }, Black { @Composable @ReadOnlyComposable - override fun getText(): String = stringResource(CommonStrings.common_black) + override fun getText(): String = stringResource(R.string.theme_black) } } diff --git a/features/preferences/impl/src/main/res/values-be/translations.xml b/features/preferences/impl/src/main/res/values-be/translations.xml index b27a22bb8d..7f487afa24 100644 --- a/features/preferences/impl/src/main/res/values-be/translations.xml +++ b/features/preferences/impl/src/main/res/values-be/translations.xml @@ -53,6 +53,9 @@ "налады сістэмы" "Сістэмныя апавяшчэнні выключаны" "Апавяшчэнні" + "Цёмная" + "Светлая" + "Сістэмная" "Выпраўленне непаладак" "Выпраўленне непаладак з апавяшчэннямі" diff --git a/features/preferences/impl/src/main/res/values-bg/translations.xml b/features/preferences/impl/src/main/res/values-bg/translations.xml index 692f0ac8f3..d5ad7facb2 100644 --- a/features/preferences/impl/src/main/res/values-bg/translations.xml +++ b/features/preferences/impl/src/main/res/values-bg/translations.xml @@ -58,6 +58,9 @@ "системни настройки" "Системните известия са изключени" "Известия" + "Тъмен" + "Светъл" + "Система" "Отстраняване на неизправности" "Отстраняване на неизправности с известията" 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 12014e7104..e51d05c455 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -87,6 +87,9 @@ Pokud budete pokračovat, některá nastavení se mohou změnit." "systémová nastavení" "Systémová oznámení byla vypnuta" "Oznámení" + "Tmavé" + "Světlý" + "Systém" "Historie push oznámení" "Odstraňování problémů" "Odstraňování problémů s upozorněními" diff --git a/features/preferences/impl/src/main/res/values-cy/translations.xml b/features/preferences/impl/src/main/res/values-cy/translations.xml index 9711eda179..e857f6aebe 100644 --- a/features/preferences/impl/src/main/res/values-cy/translations.xml +++ b/features/preferences/impl/src/main/res/values-cy/translations.xml @@ -70,6 +70,9 @@ Os ewch ymlaen, efallai y bydd rhai o\'ch gosodiadau\'n newid." "gosodiadau system" "Hysbysiadau system wedi\'u diffodd" "Hysbysiadau" + "Tywyll" + "Golau" + "System" "Hanes gwthio" "Datrys Problemau" "Hysbysiadau datrys problemau" diff --git a/features/preferences/impl/src/main/res/values-da/translations.xml b/features/preferences/impl/src/main/res/values-da/translations.xml index 2f828a4063..459ea33632 100644 --- a/features/preferences/impl/src/main/res/values-da/translations.xml +++ b/features/preferences/impl/src/main/res/values-da/translations.xml @@ -84,6 +84,9 @@ Hvis du fortsætter, kan nogle af dine indstillinger blive ændret." "systemindstillinger" "Systemmeddelelser slået fra" "Notifikationer" + "Mørkt tema" + "Lyst tema" + "System" "Push-historik" "Fejlfind" "Fejlfinding af meddelelser" diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index c3722ec88c..e35cf871a1 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -76,6 +76,9 @@ Wenn du fortfährst, können sich einige deiner Einstellungen ändern." "Systemeinstellungen" "Systembenachrichtigungen deaktiviert" "Benachrichtigungen" + "Dunkel" + "Hell" + "System" "Verlauf pushen" "Fehlerbehebung" "Fehlerbehebung für Benachrichtigungen" diff --git a/features/preferences/impl/src/main/res/values-el/translations.xml b/features/preferences/impl/src/main/res/values-el/translations.xml index 64b651114d..6db52343d3 100644 --- a/features/preferences/impl/src/main/res/values-el/translations.xml +++ b/features/preferences/impl/src/main/res/values-el/translations.xml @@ -76,6 +76,9 @@ "ρυθμίσεις συστήματος" "Ειδοποιήσεις συστήματος ανενεργές" "Ειδοποιήσεις" + "Σκοτεινό" + "Φωτεινό" + "Σύστημα" "Ιστορικό push" "Αντιμετώπιση προβλημάτων" "Αντιμετώπιση προβλημάτων ειδοποιήσεων" diff --git a/features/preferences/impl/src/main/res/values-es/translations.xml b/features/preferences/impl/src/main/res/values-es/translations.xml index afdc23d90b..dd496a1273 100644 --- a/features/preferences/impl/src/main/res/values-es/translations.xml +++ b/features/preferences/impl/src/main/res/values-es/translations.xml @@ -63,6 +63,9 @@ Si continúas, es posible que algunos de tus ajustes cambien." "ajustes del sistema" "Notificaciones del sistema desactivadas" "Notificaciones" + "Oscuro" + "Claro" + "Sistema" "Historial de notificaciones push" "Solucionar problemas" "Solucionar problemas con las notificaciones" 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 c9094f1d31..ba1ee2ac7c 100644 --- a/features/preferences/impl/src/main/res/values-et/translations.xml +++ b/features/preferences/impl/src/main/res/values-et/translations.xml @@ -81,6 +81,9 @@ Kui sa jätkad muutmist, siis võivad muutuda ka need peidetud eelistused.""süsteemi seadistusi" "Süsteemi teavitused on välja lülitatud" "Teavitused" + "Tume" + "Hele" + "Süsteem" "Tõuketeadete ajalugu" "Veaotsing" "Teavituste veaotsing" diff --git a/features/preferences/impl/src/main/res/values-eu/translations.xml b/features/preferences/impl/src/main/res/values-eu/translations.xml index 42a90e0328..42416c682b 100644 --- a/features/preferences/impl/src/main/res/values-eu/translations.xml +++ b/features/preferences/impl/src/main/res/values-eu/translations.xml @@ -55,4 +55,7 @@ "sistemaren ezarpenak" "Sistemaren jakinarazpenak desaktibatuta daude" "Jakinarazpenak" + "Iluna" + "Argia" + "Sistema" diff --git a/features/preferences/impl/src/main/res/values-fa/translations.xml b/features/preferences/impl/src/main/res/values-fa/translations.xml index 1a832ca54c..df42ca953c 100644 --- a/features/preferences/impl/src/main/res/values-fa/translations.xml +++ b/features/preferences/impl/src/main/res/values-fa/translations.xml @@ -67,6 +67,9 @@ "تنظیمات سامانه" "آگاهی‌های سامانه‌ای خاموش شدند" "آگاهی‌ها" + "تیره" + "روشن" + "سامانه" "تاریخچهٔ آگاهی‌های ارسالی" "رفع‌اشکال" "رفع‌اشکال آگاهی‌ها" diff --git a/features/preferences/impl/src/main/res/values-fi/translations.xml b/features/preferences/impl/src/main/res/values-fi/translations.xml index f1462f3bb7..97da4e138a 100644 --- a/features/preferences/impl/src/main/res/values-fi/translations.xml +++ b/features/preferences/impl/src/main/res/values-fi/translations.xml @@ -76,6 +76,9 @@ Jos jatkat, jotkin asetukset saattavat muuttua." "järjestelmäsi asetuksia" "Järjestelmän ilmoitukset on poissa päältä" "Ilmoitukset" + "Tumma" + "Vaalea" + "Järjestelmän oletus" "Push-historia" "Vianmääritys" "Ilmoitusten vianmääritys" 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 292b5bf9a5..3f97b67eb8 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -84,6 +84,9 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi "paramètres du système" "Les notifications du système sont désactivées" "Notifications" + "Sombre" + "Clair" + "Système" "Historique des Push" "Dépannage" "Dépanner les notifications" diff --git a/features/preferences/impl/src/main/res/values-hr/translations.xml b/features/preferences/impl/src/main/res/values-hr/translations.xml index a40a6feccd..d101146f8c 100644 --- a/features/preferences/impl/src/main/res/values-hr/translations.xml +++ b/features/preferences/impl/src/main/res/values-hr/translations.xml @@ -85,6 +85,9 @@ Ako nastavite, neke od vaših postavki mogu se promijeniti." "postavke sustava" "Obavijesti sustava su isključene" "Obavijesti" + "Tamno" + "Svijetlo" + "Sustav" "Povijest push obavijesti" "Rješavanje problema" "Rješavanje problema s obavijestima" 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 d75166df5e..4c3ff5c04d 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -84,6 +84,9 @@ Ha folytatja, egyes beállítások megváltozhatnak." "rendszerbeállításokat" "A rendszerértesítések ki vannak kapcsolva" "Értesítések" + "Sötét" + "Világos" + "Rendszer" "Leküldéses értesítések előzmények" "Hibaelhárítás" "Értesítések hibaelhárítása" 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 5ce794c8ac..9c853aed45 100644 --- a/features/preferences/impl/src/main/res/values-in/translations.xml +++ b/features/preferences/impl/src/main/res/values-in/translations.xml @@ -72,6 +72,9 @@ Jika Anda melanjutkan, beberapa pengaturan Anda dapat berubah." "pengaturan sistem" "Pemberitahuan sistem dimatikan" "Notifikasi" + "Gelap" + "Terang" + "Sistem" "Riwayat dorongan" "Pemecahan masalah" "Pecahkan masalah notifikasi" diff --git a/features/preferences/impl/src/main/res/values-it/translations.xml b/features/preferences/impl/src/main/res/values-it/translations.xml index 7872a2d47d..73555754fc 100644 --- a/features/preferences/impl/src/main/res/values-it/translations.xml +++ b/features/preferences/impl/src/main/res/values-it/translations.xml @@ -84,6 +84,9 @@ Se procedi, alcune delle tue impostazioni potrebbero cambiare." "impostazioni di sistema" "Notifiche di sistema disattivate" "Notifiche" + "Scuro" + "Chiaro" + "Sistema" "Cronologia push" "Risoluzione dei problemi" "Risoluzione di problemi delle notifiche" diff --git a/features/preferences/impl/src/main/res/values-ja/translations.xml b/features/preferences/impl/src/main/res/values-ja/translations.xml index 22334c4120..95380cf148 100644 --- a/features/preferences/impl/src/main/res/values-ja/translations.xml +++ b/features/preferences/impl/src/main/res/values-ja/translations.xml @@ -83,6 +83,9 @@ "システム設定" "システムで通知がオフです" "通知" + "ダーク" + "ライト" + "システム" "プッシュ履歴" "トラブルシューティング" "通知のトラブルシューティング" diff --git a/features/preferences/impl/src/main/res/values-ka/translations.xml b/features/preferences/impl/src/main/res/values-ka/translations.xml index 1efe197267..5a20e718a0 100644 --- a/features/preferences/impl/src/main/res/values-ka/translations.xml +++ b/features/preferences/impl/src/main/res/values-ka/translations.xml @@ -50,6 +50,9 @@ "სისტემის პარამეტრები" "სისტემის შეტყობინებები გამორთულია" "შეტყობინებები" + "მუქი" + "ღია" + "სისტემა" "პრობლემების გადაჭრა" "პრობლემების გადაჭრის შეტყობინებები" diff --git a/features/preferences/impl/src/main/res/values-ko/translations.xml b/features/preferences/impl/src/main/res/values-ko/translations.xml index 5e17d57a87..e2c5e8e83b 100644 --- a/features/preferences/impl/src/main/res/values-ko/translations.xml +++ b/features/preferences/impl/src/main/res/values-ko/translations.xml @@ -77,6 +77,9 @@ "시스템 설정" "시스템 알림이 꺼져 있습니다." "알림" + "다크" + "라이트" + "시스템" "푸시 기록" "문제 해결" "문제 해결 알림" diff --git a/features/preferences/impl/src/main/res/values-nb/translations.xml b/features/preferences/impl/src/main/res/values-nb/translations.xml index 90ec12a1a1..ac67e04259 100644 --- a/features/preferences/impl/src/main/res/values-nb/translations.xml +++ b/features/preferences/impl/src/main/res/values-nb/translations.xml @@ -76,6 +76,9 @@ Hvis du fortsetter, kan noen av innstillingene dine endres." "systeminnstillinger" "Systemvarsler er slått av" "Varslinger" + "Mørk" + "Lys" + "System" "Push-historikk" "Feilsøk" "Feilsøk varsler" diff --git a/features/preferences/impl/src/main/res/values-nl/translations.xml b/features/preferences/impl/src/main/res/values-nl/translations.xml index 28614965c5..c5c439a0c3 100644 --- a/features/preferences/impl/src/main/res/values-nl/translations.xml +++ b/features/preferences/impl/src/main/res/values-nl/translations.xml @@ -54,6 +54,9 @@ Als je doorgaat, kunnen sommige van je instellingen veranderen." "systeeminstellingen" "Systeemmeldingen uitgeschakeld" "Meldingen" + "Donker" + "Licht" + "Systeem" "Problemen oplossen" "Problemen met meldingen oplossen" 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 606a79df0d..3fc2a3eac8 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -85,6 +85,9 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "ustawienia systemowe" "Powiadomienia systemowe wyłączone" "Powiadomienia" + "Ciemny" + "Jasny" + "System" "Historia powiadomień Push" "Rozwiązywanie problemów" "Rozwiązywanie problemów powiadomień" diff --git a/features/preferences/impl/src/main/res/values-pt-rBR/translations.xml b/features/preferences/impl/src/main/res/values-pt-rBR/translations.xml index f6e1bc90ba..be354681f5 100644 --- a/features/preferences/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/preferences/impl/src/main/res/values-pt-rBR/translations.xml @@ -76,6 +76,9 @@ Se você continuar, algumas de suas configurações poderão mudar." "configurações do seu sistema" "Notificações do sistema desativadas" "Notificações" + "Escuro" + "Claro" + "Sistema" "Histórico de push" "Solução de problemas" "Solucionar problemas de notificações" 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 ffd38b961f..0e27d58de5 100644 --- a/features/preferences/impl/src/main/res/values-pt/translations.xml +++ b/features/preferences/impl/src/main/res/values-pt/translations.xml @@ -76,6 +76,9 @@ Se prosseguires, algumas delas podem ser alteradas." "configurações do sistema" "Notificações do sistema desativadas" "Notificações" + "Escuro" + "Claro" + "Sistema" "Histórico de push" "Resolução de problemas" "Corrigir notificações" diff --git a/features/preferences/impl/src/main/res/values-ro/translations.xml b/features/preferences/impl/src/main/res/values-ro/translations.xml index 6f8e41c5b9..18f8326bf3 100644 --- a/features/preferences/impl/src/main/res/values-ro/translations.xml +++ b/features/preferences/impl/src/main/res/values-ro/translations.xml @@ -78,6 +78,9 @@ Dacă continuați, unele dintre setările dumneavoastră pot fi modificate.""Setări de sistem" "Notificările de sistem sunt dezactivate" "Notificări" + "Întunecat" + "Deschis" + "Sistem" "Istoricul notificărilor" "Depanare" "Depanați notificările" 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 77be6fd8db..edb98feee8 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -76,6 +76,9 @@ "системные настройки" "Системные уведомления выключены" "Уведомления" + "Темная" + "Светлое" + "Системное" "История уведомлений" "Устранение неполадок" "Уведомления об устранении неполадок" diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index 9968c6d4cb..530fccd90b 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -78,6 +78,9 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""nastavenia systému" "Systémové oznámenia sú vypnuté" "Oznámenia" + "Tmavý" + "Svetlý" + "Systém" "História push oznámení" "Riešenie problémov" "Oznámenia riešení problémov" diff --git a/features/preferences/impl/src/main/res/values-sv/translations.xml b/features/preferences/impl/src/main/res/values-sv/translations.xml index e25e36a44f..109000440f 100644 --- a/features/preferences/impl/src/main/res/values-sv/translations.xml +++ b/features/preferences/impl/src/main/res/values-sv/translations.xml @@ -70,6 +70,9 @@ Om du fortsätter kan vissa av dina inställningar ändras." "systeminställningar" "Systemaviseringar avstängda" "Aviseringar" + "Mörkt" + "Ljust" + "System" "Push-historik" "Felsök" "Felsök aviseringar" diff --git a/features/preferences/impl/src/main/res/values-tr/translations.xml b/features/preferences/impl/src/main/res/values-tr/translations.xml index 0df8500fda..78e9018554 100644 --- a/features/preferences/impl/src/main/res/values-tr/translations.xml +++ b/features/preferences/impl/src/main/res/values-tr/translations.xml @@ -74,6 +74,9 @@ Devam ederseniz, bazı ayarlarınız değişebilir." "si̇stem ayarları" "Sistem bildirimleri kapalı" "Bildirimler" + "Koyu" + "Aydınlık" + "Sistem" "Sorun gider" "Sorun Giderme Bildirimleri" diff --git a/features/preferences/impl/src/main/res/values-uk/translations.xml b/features/preferences/impl/src/main/res/values-uk/translations.xml index eeffc97328..4d8d738122 100644 --- a/features/preferences/impl/src/main/res/values-uk/translations.xml +++ b/features/preferences/impl/src/main/res/values-uk/translations.xml @@ -85,6 +85,9 @@ "системні налаштування" "Системні сповіщення вимкнені" "Сповіщення" + "Темна" + "Світла" + "Системна" "Історія push-сповіщень" "Усунення несправностей" "Усунення неполадок сповіщень" diff --git a/features/preferences/impl/src/main/res/values-ur/translations.xml b/features/preferences/impl/src/main/res/values-ur/translations.xml index 470763e877..35b8f4aa4e 100644 --- a/features/preferences/impl/src/main/res/values-ur/translations.xml +++ b/features/preferences/impl/src/main/res/values-ur/translations.xml @@ -53,6 +53,9 @@ "نظام کی ترتیبات" "نظام کی اطلاعات بند کر دی گئیں" "اطلاعات" + "اندھیرا" + "روشنی" + "نظام" "ازالہ کریں" "اطلاعات کا ازالہ کریں" diff --git a/features/preferences/impl/src/main/res/values-uz/translations.xml b/features/preferences/impl/src/main/res/values-uz/translations.xml index 9c81fc3daa..884027f9b8 100644 --- a/features/preferences/impl/src/main/res/values-uz/translations.xml +++ b/features/preferences/impl/src/main/res/values-uz/translations.xml @@ -76,6 +76,9 @@ Davom ettirsangiz, baʼzi sozlamalaringiz oʻzgarishi mumkin." "tizim sozlamalari" "Tizim bildirishnomalari o\'chirilgan" "Bildirishnomalar" + "Tungi" + "Nur" + "Tizim" "Bildirishnoma tarixi" "Muammolarni bartaraf etish" "Bildirishnomalar bilan bog‘liq muammolarni bartaraf etish" diff --git a/features/preferences/impl/src/main/res/values-vi/translations.xml b/features/preferences/impl/src/main/res/values-vi/translations.xml index 9f24244b7e..9889b1697a 100644 --- a/features/preferences/impl/src/main/res/values-vi/translations.xml +++ b/features/preferences/impl/src/main/res/values-vi/translations.xml @@ -61,6 +61,9 @@ Nếu bạn tiếp tục, một số cài đặt của bạn có thể thay đ "cài đặt hệ thống" "Thông báo hệ thống đã tắt" "Thông báo" + "Tối" + "Sáng" + "Hệ thống" "Khắc phục sự cố" "Khắc phục sự cố thông báo" diff --git a/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml index 634911100a..03892ce980 100644 --- a/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml @@ -76,6 +76,9 @@ "系統設定" "已關閉系統通知" "通知" + "深色" + "淺色" + "系統" "推播通知歷史紀錄" "疑難排解" "疑難排解通知" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index 41eb147141..e91bc58752 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -83,6 +83,9 @@ "系统设置" "系统通知已关闭" "通知" + "深色" + "浅色" + "系统" "推送历史" "排查问题" "排查通知问题" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index d5abe1df6a..a431139fd6 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -84,6 +84,10 @@ If you proceed, some of your settings may change." "system settings" "System notifications turned off" "Notifications" + "Black" + "Dark" + "Light" + "System" "Push history" "Troubleshoot" "Troubleshoot notifications" diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt index 4823a7c26e..302a209578 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsViewTest.kt @@ -62,7 +62,7 @@ class AdvancedSettingsViewTest { ), ) clickOn(CommonStrings.common_appearance) - clickOn(CommonStrings.common_dark) + clickOn(R.string.theme_dark) eventsRecorder.assertSingle(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark)) } @@ -75,7 +75,7 @@ class AdvancedSettingsViewTest { ) clickOn(CommonStrings.common_appearance) run { - val text = activity!!.getString(CommonStrings.common_black) + val text = activity!!.getString(R.string.theme_black) onNodeWithText(text).assertExists() } } @@ -88,7 +88,7 @@ class AdvancedSettingsViewTest { ), ) clickOn(CommonStrings.common_appearance) - assertNoNodeWithText(CommonStrings.common_black) + assertNoNodeWithText(R.string.theme_black) } @Test 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 f106b46e6c..f550ce1075 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -170,7 +170,6 @@ "Аўтарскае права" "Стварэнне пакоя…" "Выйшаў з пакоя" - "Цёмная" "Памылка расшыфроўкі" "Параметры распрацоўшчыка" "Прамы чат" @@ -197,7 +196,6 @@ "Усталяваць APK" "Гэты Matrix ID не знойдзены, таму запрашэнне можа быць не атрымана." "Пакінуць пакой" - "Светлая" "Спасылка скапіравана ў буфер абмену" "Загрузка…" @@ -272,7 +270,6 @@ "Поспех" "Прапановы" "Сінхранізацыя" - "Сістэмная" "Тэкст" "Паведамленні трэціх асоб" "Гутарка" diff --git a/libraries/ui-strings/src/main/res/values-bg/translations.xml b/libraries/ui-strings/src/main/res/values-bg/translations.xml index 596b7b3e34..3f38b1991f 100644 --- a/libraries/ui-strings/src/main/res/values-bg/translations.xml +++ b/libraries/ui-strings/src/main/res/values-bg/translations.xml @@ -145,7 +145,6 @@ "Създаване на стая…" "Стаята е напусната" "Пространството е напуснато" - "Тъмен" "Грешка при разшифроване" "Описание" "Опции за разработчици" @@ -176,7 +175,6 @@ "Този Matrix ID не може да бъде намерен, така че поканата може да не бъде получена." "Стаята се напуска" "Пространството се напуска" - "Светъл" "Връзката е копирана в клипборда" "Зарежда се…" "Зарежда се още…" @@ -269,7 +267,6 @@ "Успешно" "Предложения" "Синхронизиране" - "Система" "Текст" "Уведомления от трети страни" "Нишка" 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 4a8623f5dc..36bd188c2a 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -215,7 +215,6 @@ "Místnost opuštěna" "Opustit prostor" "Pozvánka odmítnuta" - "Tmavé" "Chyba dešifrování" "Popis" "Možnosti pro vývojáře" @@ -255,7 +254,6 @@ Důvod: %1$s." "Tento Matrix identifikátor nelze najít, takže pozvánka nemusí být přijata." "Opuštění místnosti" "Opuštění prostoru" - "Světlý" "Řádek zkopírován do schránky" "Odkaz zkopírován do schránky" "Připojit nové zařízení" @@ -380,7 +378,6 @@ Důvod: %1$s." "Doporučeno" "Návrhy" "Synchronizace" - "Systém" "Text" "Oznámení třetích stran" "Vlákno" diff --git a/libraries/ui-strings/src/main/res/values-cy/translations.xml b/libraries/ui-strings/src/main/res/values-cy/translations.xml index 98538f517d..00e2a39297 100644 --- a/libraries/ui-strings/src/main/res/values-cy/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cy/translations.xml @@ -198,7 +198,6 @@ "Wedi gadael yr ystafell" "Gofod chwith" "Wedi gwrthod y gwahoddiad" - "Tywyll" "Gwall dadgryptio" "Disgrifiad" "Dewisiadau datblygwr" @@ -235,7 +234,6 @@ Rheswm: %1$s." "Gosod APK" "Nid oes modd dod o hyd i\'r ID Matrics hwn, felly mae\'n bosibl na fydd y gwahoddiad yn cael ei dderbyn." "Gadael ystafell" - "Golau" "Llinell wedi\'i chopïo i\'r clipfwrdd" "Dolen wedi\'i chopïo i\'r clipfwrdd" "Yn Llwytho…" @@ -359,7 +357,6 @@ Rheswm: %1$s." "Llwyddiant" "Awgrymiadau" "Cydweddu" - "System" "Testun" "Hysbysiadau trydydd parti" "Edefyn" diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index 0837d0bb33..ce79b8972b 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -217,7 +217,6 @@ "Forlod rummet" "Forlod klynge" "Invitationen blev afvist" - "Mørkt tema" "Fejl under dekryptering" "Beskrivelse" "Indstillinger for udviklere" @@ -257,7 +256,6 @@ "Dette Matrix-ID kan ikke findes, så invitationen modtages muligvis ikke." "Forlader rummet" "Forlader klynge" - "Lyst tema" "Linje kopieret til udklipsholder" "Linket er kopieret til udklipsholderen" "Forbind ny enhed" @@ -376,7 +374,6 @@ "Forslag" "Forslag" "Synkroniserer" - "System" "Tekst" "Tredjepartsmeddelelser" "Tråd" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 1a9190568c..5b7f52cf4e 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -209,7 +209,6 @@ "Hat den Chat verlassen" "Space verlassen" "Einladung abgelehnt" - "Dunkel" "Dekodierungsfehler" "Beschreibung" "Entwickleroptionen" @@ -249,7 +248,6 @@ Grund: %1$s." "Diese Matrix Kennung wurde nicht gefunden, daher wird die Einladung möglicherweise nicht empfangen." "Chat verlassen" "Space wird verlassen" - "Hell" "Zeile in die Zwischenablage kopiert" "Link in die Zwischenablage kopiert" "Neues Gerät verknüpfen" @@ -369,7 +367,6 @@ Grund: %1$s." "Empfohlen" "Vorschläge" "Synchronisieren" - "System" "Text" "Hinweise von Drittanbietern" "Thread" 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 e5d6d0d1f5..ec14b73e04 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -209,7 +209,6 @@ "Αποχώρησε από την αίθουσα" "Αποχωρήσατε από τον χώρο" "Η πρόσκληση απορρίφθηκε" - "Σκοτεινό" "Σφάλμα αποκρυπτογράφησης" "Περιγραφή" "Επιλογές προγραμματιστή" @@ -249,7 +248,6 @@ "Αυτό το Matrix ID δεν μπορεί να βρεθεί, επομένως η πρόσκληση ενδέχεται να μην ληφθεί." "Αποχώρηση από την αίθουσα" "Αποχωρείτε από τον χώρο" - "Φωτεινό" "Η γραμμή αντιγράφηκε στο πρόχειρο" "Ο σύνδεσμος αντιγράφηκε στο πρόχειρο" "Σύνδεση νέας συσκευής" @@ -369,7 +367,6 @@ "Προτεινόμενο" "Προτάσεις" "Συγχρονισμός" - "Σύστημα" "Κείμενο" "Ειδοποιήσεις τρίτων" "Νήμα" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 1dc69b127c..b03223a70e 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -165,7 +165,6 @@ "Solicitud cancelada" "Saliste de la sala" "Invitación rechazada" - "Oscuro" "Error de descifrado" "Opciones de desarrollador" "ID de dispositivo" @@ -201,7 +200,6 @@ Motivo: %1$s." "Instalar APK" "No se encontró este ID de Matrix, por lo que es posible que no se reciba la invitación." "Saliendo de la sala" - "Claro" "Línea copiada al portapapeles" "Enlace copiado al portapapeles" "Cargando…" @@ -280,7 +278,6 @@ Motivo: %1$s." "Terminado" "Sugerencias" "Sincronizando" - "Sistema" "Texto" "Avisos de terceros" "Hilo" 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 6004792f51..1669abc4e0 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -216,7 +216,6 @@ "Lahkus jututoast" "Lahkus kogukonnast" "Keeldusid kutsest" - "Tume" "Dekrüptimisviga" "Kirjeldus" "Arendaja valikud" @@ -255,7 +254,6 @@ Põhjus: %1$s." "Sellist Matrix\'i kasutajatunnust ei õnnestu leida, seega sõnumit ilmselt keegi kätte ei saa." "Oled lahkumas jututoast" "Oled lahkumas kogukonnast" - "Hele" "Rida on kopeeritud lõikelauale" "Link on kopeeritud lõikelauale" "Seo uus seade" @@ -374,7 +372,6 @@ Põhjus: %1$s." "Soovitatud" "Soovitused" "Sünkroniseerime" - "Süsteem" "Tekst" "Kolmandate osapoolte teatised" "Jutulõng" diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml index 7e9d3a05fe..0c046b8bb4 100644 --- a/libraries/ui-strings/src/main/res/values-eu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -169,7 +169,6 @@ "Eskaera bertan behera utzi da" "Gelatik atera da" "Gonbidapenari uko egin zaio" - "Iluna" "Deszifratze-errorea" "Garapen aukerak" "Gailuaren IDa" @@ -204,7 +203,6 @@ Arrazoia: %1$s." "Instalatu APK" "Matrix IDa ezin da topatu eta, beraz, litekeena da gonbidapena ez jasotzea." "Gelatik ateratzen" - "Argia" "Lerroa arbelean kopiatu da" "Esteka arbelean kopiatu da" "Kargatzen…" @@ -287,7 +285,6 @@ Arrazoia: %1$s." "Arrakasta" "Iradokizunak" "Sinkronizatzen" - "Sistema" "Testua" "Hirugarrenei buruzko oharrak" "Haria" diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index 418ca96419..c14f281f0d 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -185,7 +185,6 @@ "اتاق را ترک کرد" "فضا را ترک کرد" "دعوت لغو شد" - "تیره" "خطای رمزگشایی" "توضیح" "گزینه‌های توسعه دهنده" @@ -220,7 +219,6 @@ "شناسهٔ ماتریکس نتوانست پیدا شود. ممکن است دعوت نرسیده باشد." "ترک کردن اتاق" "ترک کردن فضا" - "روشن" "خط در تخته‌گیره رونوشت شد" "پیوند در تخته‌گیره رونوشت شد" "بار کردن…" @@ -308,7 +306,6 @@ "موفّقیت" "پیشنهادها" "هم‌گام ساختن" - "سامانه" "متن" "تذکّرهای سوم‌شخص" "رشته" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 1a7f6c50c9..d5093590ca 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -210,7 +210,6 @@ "Poistuit huoneesta" "Poistuit tilasta" "Kutsu hylätty" - "Tumma" "Salauksen purkuvirhe" "Kuvaus" "Kehittäjän asetukset" @@ -250,7 +249,6 @@ Syy: %1$s." "Tätä Matrix-tunnusta ei löytynyt, joten kutsu ei välttämättä mene perille." "Poistutaan huoneesta" "Poistutaan tilasta" - "Vaalea" "Rivi kopioitu leikepöydälle" "Linkki kopioitu leikepöydälle" "Yhdistä uusi laite" @@ -370,7 +368,6 @@ Syy: %1$s." "Ehdotettu" "Ehdotukset" "Synkronoidaan" - "Järjestelmän oletus" "Teksti" "Kolmannen osapuolen ilmoitukset" "Viestiketju" 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 5cb21b772f..545cf66c73 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -217,7 +217,6 @@ "Vous avez quitté le salon" "Vous avez quitté l’espace" "Invitation refusée" - "Sombre" "Erreur de déchiffrement" "Description" "Options pour les développeurs" @@ -257,7 +256,6 @@ Raison : %1$s." "Cet identifiant Matrix est introuvable, il est donc possible que l’invitation ne soit pas reçue." "Quitter le salon…" "En train de quitter l’espace" - "Clair" "Ligne copiée dans le presse-papiers" "Lien copié dans le presse-papiers" "Associer un nouvel appareil" @@ -377,7 +375,6 @@ Raison : %1$s." "Recommandé" "Suggestions" "Synchronisation" - "Système" "Texte" "Avis de tiers" "Fil de discussion" diff --git a/libraries/ui-strings/src/main/res/values-hr/translations.xml b/libraries/ui-strings/src/main/res/values-hr/translations.xml index bb6cf79187..369d8ccce4 100644 --- a/libraries/ui-strings/src/main/res/values-hr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hr/translations.xml @@ -219,7 +219,6 @@ "Napustio/la je sobu" "Napušteni prostor" "Poziv je odbijen" - "Tamno" "Pogreška kod dešifriranja" "Opis" "Mogućnosti za razvojne inženjere" @@ -259,7 +258,6 @@ Razlog: %1$s ." "Ovaj Matrix ID nije moguće pronaći, pa se pozivnica možda neće primiti." "Izlazak iz sobe" "Napuštanje prostora" - "Svijetlo" "Redak je kopiran u međuspremnik" "Poveznica je kopirana u međuspremnik." "Poveži novi uređaj" @@ -385,7 +383,6 @@ Razlog: %1$s ." "Preporučeno" "Prijedlozi" "Sinkronizacija" - "Sustav" "Tekst" "Obavijesti trećih strana" "Nit" 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 c477a6b6ba..7acfb90ef7 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -218,7 +218,6 @@ "Elhagyta a szobát" "Tér elhagyva" "Meghívás elutasítva" - "Sötét" "Visszafejtési hiba" "Leírás" "Fejlesztői beállítások" @@ -258,7 +257,6 @@ Ok: %1$s." "Ez a Matrix-azonosító nem található, ezért előfordulhat, hogy a meghívó nem érkezik meg." "Szoba elhagyása" "Tér elhagyása" - "Világos" "A sor a vágólapra másolva" "Hivatkozás a vágólapra másolva" "Új eszköz összekapcsolása" @@ -377,7 +375,6 @@ Ok: %1$s." "Javaslat" "Javaslatok" "Szinkronizálás" - "Rendszer" "Szöveg" "Harmadik felek nyilatkozatai" "Üzenetszál" 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 ec5a582838..51d78530a8 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -185,7 +185,6 @@ "Permintaan dibatalkan" "Keluar dari ruangan" "Undangan ditolak" - "Gelap" "Kesalahan dekripsi" "Deskripsi" "Opsi pengembang" @@ -222,7 +221,6 @@ Alasan: %1$s." "Pasang APK" "ID Matrix ini tidak dapat ditemukan, sehingga undangan mungkin tidak diterima." "Meninggalkan ruangan" - "Terang" "Baris disalin ke papan klip" "Tautan disalin ke papan klip" "Memuat…" @@ -308,7 +306,6 @@ Alasan: %1$s." "Berhasil" "Saran" "Menyinkronkan" - "Sistem" "Teks" "Pemberitahuan pihak ketiga" "Utas" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index cdc523a5ab..d1ec78d920 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -218,7 +218,6 @@ "Hai lasciato la stanza" "Hai lasciato lo spazio" "Invito rifiutato" - "Scuro" "Errore di decrittazione" "Descrizione" "Opzioni sviluppatore" @@ -258,7 +257,6 @@ Motivo:. %1$s" "Questo ID Matrix non può essere trovato, quindi l\'invito potrebbe non essere ricevuto." "Lascio la stanza" "Uscendo dallo spazio" - "Chiaro" "Riga copiata negli appunti" "Collegamento copiato negli appunti" "Collega un nuovo dispositivo" @@ -378,7 +376,6 @@ Motivo:. %1$s" "Suggeriti" "Suggerimenti" "Sincronizzazione" - "Sistema" "Testo" "Comunicazioni di terze parti" "Discussione" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index e25ca0cf4e..c62c579f32 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -216,7 +216,6 @@ "ルームを退出しました" "スペースから退出しました" "招待は却下されました" - "ダーク" "復号化エラー" "詳細" "開発者向けオプション" @@ -256,7 +255,6 @@ "このMatrix IDは見つからないため、招待が届かない可能性があります。" "ルームを退出しています" "スペースを退出しています" - "ライト" "行をクリップボードにコピーしました" "リンクをクリップボードにコピーしました" "新しい端末から接続" @@ -369,7 +367,6 @@ "推奨される" "提案" "同期中" - "システム" "テキスト" "第三者に関する通知" "スレッド" diff --git a/libraries/ui-strings/src/main/res/values-ka/translations.xml b/libraries/ui-strings/src/main/res/values-ka/translations.xml index aa7659dde2..2be4e43d61 100644 --- a/libraries/ui-strings/src/main/res/values-ka/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ka/translations.xml @@ -122,7 +122,6 @@ "საავტორო უფლება" "ოთახის შექმნა…" "დატოვა ოთახი" - "მუქი" "გაშიფვრის შეცდომა" "დეველოპერის პარამეტრები" "პირდაპირი ჩატი" @@ -145,7 +144,6 @@ "დააინსტალირეთ APK" "ეს Matrix ID ვერ მოიძებნა, ამიტომ მოწვევა შეიძლება არ იყოს მიღებული." "ოთახის დატოვება" - "ღია" "ბმული კოპირებულია გაცვლის ბუფერში" "იტვირთება…" @@ -212,7 +210,6 @@ "წარმატება" "შეთავაზებები" "სინქრონიზაცია" - "სისტემა" "ტექსტი" "მესამე პირის შენიშვნები" "თემა" diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 3a029e20fc..303e8ba929 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -207,7 +207,6 @@ "방 떠남" "스페이스 떠남" "초대 거부됨" - "다크" "복호화 오류" "설명" "개발자 설정" @@ -247,7 +246,6 @@ "Matrix ID를 찾을 수 없기 때문에 초대가 수신되지 않을 수도 있습니다." "방을 떠나는 중" "스페이스 떠나는 중" - "라이트" "줄이 클립보드에 복사되었습니다." "링크가 클립보드에 복사됨" "새 기기 연결" @@ -360,7 +358,6 @@ "공유된 스페이스" "제안" "동기화 중" - "시스템" "글자" "제3자 고지" "스레드" diff --git a/libraries/ui-strings/src/main/res/values-nb/translations.xml b/libraries/ui-strings/src/main/res/values-nb/translations.xml index a0bb1b1a16..aa594f8027 100644 --- a/libraries/ui-strings/src/main/res/values-nb/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nb/translations.xml @@ -207,7 +207,6 @@ "Forlot rommet" "Forlot område" "Invitasjon avslått" - "Mørk" "Dekrypteringsfeil" "Beskrivelse" "Alternativer for utviklere" @@ -245,7 +244,6 @@ "Finner ikke denne Matrix-IDen, så invitasjonen blir kanskje ikke mottatt." "Forlater rommet" "Forlater området" - "Lys" "Linje kopiert til utklippstavlen" "Lenke kopiert til utklippstavlen" "Koble til ny enhet" @@ -360,7 +358,6 @@ "Foreslått" "Forslag" "Synkroniserer" - "System" "Tekst" "Varsler fra tredjeparter" "Tråd" 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 ab39365f8e..3edeeb00a0 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -172,7 +172,6 @@ "Kamer maken…" "Heeft de kamer verlaten" "Uitnodiging geweigerd" - "Donker" "Decryptie fout" "Ontwikkelaarsopties" "Apparaat-ID" @@ -200,7 +199,6 @@ Reden: %1$s." "APK installeren" "Deze Matrix-ID kan niet worden gevonden, dus de uitnodiging is mogelijk niet ontvangen." "De kamer verlaten" - "Licht" "Link gekopieerd naar klembord" "Laden…" @@ -278,7 +276,6 @@ Reden: %1$s." "Geslaagd" "Suggesties" "Synchroniseren" - "Systeem" "Tekst" "Kennisgevingen van derden" "Gesprek" 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 1a9a45cd94..fb6b06daf7 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -220,7 +220,6 @@ "Opuszczono pokój" "Opuścił przestrzeń" "Odrzucono zaproszenie" - "Ciemny" "Błąd deszyfrowania" "Opis" "Opcje programisty" @@ -260,7 +259,6 @@ Powód: %1$s." "Nie można znaleźć identyfikatora Matrix ID, zaproszenie mogło nie dotrzeć." "Opuszczanie pokoju" "Opuszczam przestrzeń" - "Jasny" "Wiersz skopiowany do schowka" "Link został skopiowany do schowka" "Powiąż nowe urządzenie" @@ -387,7 +385,6 @@ Powód: %1$s." "Polecane" "Sugestie" "Synchronizuję" - "System" "Tekst" "Informacje stron trzecich" "Wątek" diff --git a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml index 68035e0cc2..f35aca5a58 100644 --- a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml @@ -200,7 +200,6 @@ "Saiu da sala" "Saiu do espaço" "Convite recusado" - "Escuro" "Erro de descriptografia" "Descrição" "Opções de desenvolvedor" @@ -238,7 +237,6 @@ Motivo:​ %1$s." "Este ID Matrix não foi encontrado, então o convite pode não ser recebido" "Saindo da sala" "Saindo do espaço" - "Claro" "Linha copiada para a área de transferência" "Link copiado para área de transferência" "Vincular novo dispositivo" @@ -350,7 +348,6 @@ Motivo:​ %1$s." "Sugerido" "Sugestões" "Sincronizando" - "Sistema" "Texto" "Comunicados de terceiros" "Tópico" 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 d82855223d..0acc939565 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -195,7 +195,6 @@ "Saíste da sala" "Saíste do espaço" "Convite rejeitado" - "Escuro" "Erro de decifragem" "Descrição" "Opções de programador" @@ -233,7 +232,6 @@ Razão: %1$s." "Não foi possível encontrar este ID Matrix, portanto o convite pode não ser recebido." "A sair da sala" "A sair do espaço" - "Claro" "Linha copiada para a área de transferência" "Ligação copiada para a área de transferência" "A carregar…" @@ -334,7 +332,6 @@ Razão: %1$s." "Sucesso" "Sugestões" "A sincronizar…" - "Sistema" "Texto" "Avisos de terceiros" "Tópico" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 33a39278a4..35930b694e 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -198,7 +198,6 @@ "Ați parăsit camera" "S-a părăsit spațiul" "Invitația a fost refuzată" - "Întunecat" "Eroare de decriptare" "Descriere" "Opțiuni programator" @@ -236,7 +235,6 @@ Motiv:%1$s." "Nu am putut valida ID-ul Matrix al acestui utilizator. Este posibil ca invitația să nu fi fost trimisă." "Se părăsește conversația" "Se părăsește spațiul" - "Deschis" "Linie copiată în clipboard" "Linkul a fost copiat în clipboard" "Conectați un dispozitiv nou" @@ -347,7 +345,6 @@ Motiv:%1$s." "Succes" "Sugestii" "Se sincronizează…" - "Sistem" "Text" "Notificări despre software de la terți" "Fir" 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 fe852dcfd0..26e1dcfe8d 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -212,7 +212,6 @@ "Покинул комнату" "Покинуть пространство" "Приглашение отклонено" - "Темная" "Ошибка расшифровки" "Описание" "Для разработчиков" @@ -252,7 +251,6 @@ "Идентификатор Matrix ID не найден, приглашение может быть не получено." "Покидаем комнату" "Покидаем пространство" - "Светлое" "Строка скопирована в буфер обмена" "Ссылка скопирована в буфер обмена" "Привязать новое устройство" @@ -379,7 +377,6 @@ "Рекомендуемые" "Предложения" "Синхронизация" - "Системное" "Текст" "Уведомления о третьих лицах" "Обсуждение" 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 208016e363..11f4b22005 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -206,7 +206,6 @@ "Opustil/a miestnosť" "Opustil priestor" "Pozvánka bola odmietnutá" - "Tmavý" "Chyba dešifrovania" "Popis" "Možnosti pre vývojárov" @@ -244,7 +243,6 @@ Dôvod: %1$s." "Toto Matrix ID sa nedá nájsť, takže pozvánka nemusí byť prijatá." "Opustenie miestnosti" "Opúšťanie priestoru" - "Svetlý" "Riadok skopírovaný do schránky" "Odkaz bol skopírovaný do schránky" "Prepojiť nové zariadenie" @@ -363,7 +361,6 @@ Dôvod: %1$s." "Navrhované" "Návrhy" "Synchronizuje sa" - "Systém" "Text" "Oznámenia tretích strán" "Vlákno" diff --git a/libraries/ui-strings/src/main/res/values-sv/translations.xml b/libraries/ui-strings/src/main/res/values-sv/translations.xml index afc58de8a5..28dd63d425 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -187,7 +187,6 @@ "Begäran avbruten" "Lämnade rummet" "Inbjudan avvisad" - "Mörkt" "Avkrypteringsfel" "Beskrivning" "Utvecklaralternativ" @@ -224,7 +223,6 @@ Anledning:%1$s." "Installera APK" "Det här Matrix-ID:t kan inte hittas, så inbjudan kanske inte tas emot." "Lämnar rummet" - "Ljust" "Rad kopierad till klippbordet" "Länk kopierad till klippbordet" "Laddar …" @@ -322,7 +320,6 @@ Anledning:%1$s." "Lyckades" "Förslag" "Synkar" - "System" "Text" "Meddelanden från tredje part" "Tråd" diff --git a/libraries/ui-strings/src/main/res/values-tr/translations.xml b/libraries/ui-strings/src/main/res/values-tr/translations.xml index a6d0b3e198..d500b57507 100644 --- a/libraries/ui-strings/src/main/res/values-tr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-tr/translations.xml @@ -175,7 +175,6 @@ "İstek iptal edildi" "Sol oda" "Davet reddedildi" - "Koyu" "Şifre çözme hatası" "Geliştirici seçenekleri" "Cihaz Kimliği" @@ -211,7 +210,6 @@ Neden: %1$s." "APK\'yı yükleyin" "Bu Matrix Kimliği bulunamıyor, bu nedenle davet alınmayabilir." "Odadan ayrılma" - "Aydınlık" "Metin panoya kopyalandı" "Bağlantı panoya kopyalandı" "Yükleniyor…" @@ -295,7 +293,6 @@ Neden: %1$s." "Başarılı" "Öneriler" "Senkronizasyon" - "Sistem" "Metin" "Üçüncü taraf bildirimleri" "Konu" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index a9e1d14b40..d7b18bf1ae 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -220,7 +220,6 @@ "Виходить з кімнати" "Вийшов із простору" "Запрошення відхилено" - "Темна" "Помилка розшифрування" "Опис" "Налаштування розробника" @@ -260,7 +259,6 @@ "Цей Matrix-ID не знайдено, тому запрошення може не бути отримано." "Вихід з кімнати" "Вихід з простору" - "Світла" "Рядок скопійовано до буфера обміну" "Посилання скопійовано в буфер обміну" "Під\'єднати новий пристрій" @@ -387,7 +385,6 @@ "Запропоновано" "Пропозиції" "Синхронізація" - "Системна" "Текст" "Повідомлення третіх сторін" "Гілка" diff --git a/libraries/ui-strings/src/main/res/values-ur/translations.xml b/libraries/ui-strings/src/main/res/values-ur/translations.xml index 7bf138deb5..accba4556c 100644 --- a/libraries/ui-strings/src/main/res/values-ur/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ur/translations.xml @@ -141,7 +141,6 @@ "حقوقِ طبع و نشر" "کمرہ تخلیق کررہاہے…" "کمرہ چھوڑ لیا" - "اندھیرا" "رمزکشائی کی خرابی" "مطور اختیارات" "براہ راست گفتگو" @@ -168,7 +167,6 @@ "APK تنصیب کریں" "یہ میٹرکس شناخت نہیں مل سکتی، تو ہو سکتا ہے کہ دعوت نامہ موصول نہ ہو۔" "کمرہ چھوڑنا" - "روشنی" "ربط تختہ تراشہ پر نقل کردا گیا" "لاد رہا ہے…" @@ -240,7 +238,6 @@ "کامیابی" "تجاویز" "ہمسات سازی" - "نظام" "متن" "فریق ثالث کے اشعارات" "دھاگہ" diff --git a/libraries/ui-strings/src/main/res/values-uz/translations.xml b/libraries/ui-strings/src/main/res/values-uz/translations.xml index 50d14ef4d4..63e1756a23 100644 --- a/libraries/ui-strings/src/main/res/values-uz/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uz/translations.xml @@ -210,7 +210,6 @@ "Xonani tark etdi" "Tar etilgan maydon" "Taklif rad etildi" - "Tungi" "Shifrni ochish xatosi" "Tavsif" "Dasturchi variantlari" @@ -250,7 +249,6 @@ Sababi:%1$s." "Ushbu Matrix identifikatori topilmadi, shuning uchun taklif qabul qilinmasligi mumkin." "Xonadan chiqish" "Maydonni tark etish" - "Nur" "Satr vaqtinchalik xotiraga nusxalandi" "Havola vaqtinchalik xotiraga nusxalandi" "Yangi qurilmani ulang" @@ -370,7 +368,6 @@ Sababi:%1$s." "Tavsiya etilgan" "Tavsiyalar" "Sinxronlash" - "Tizim" "Matn" "Uchinchi tomon bildirishnomalari" "Ip" diff --git a/libraries/ui-strings/src/main/res/values-vi/translations.xml b/libraries/ui-strings/src/main/res/values-vi/translations.xml index 825ff12bea..5cfbe26fba 100644 --- a/libraries/ui-strings/src/main/res/values-vi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-vi/translations.xml @@ -208,7 +208,6 @@ "Đã rời khỏi phòng" "Rời khỏi không gian" "Lời mời bị từ chối" - "Tối" "Lỗi khi giải mã" "Mô tả" "Tùy chọn nhà phát triển" @@ -248,7 +247,6 @@ Lý do: %1$s ." "Không tìm thấy Matrix ID này, nên lời mời có thể chưa được nhận." "Rời khỏi phòng" "Rời khỏi không gian" - "Sáng" "Đã sao chép dòng" "Đã chép liên kết vào bộ nhớ tạm" "Liên kết thiết bị mới" @@ -359,7 +357,6 @@ Lý do: %1$s ." "Gợi ý" "Gợi ý" "Đang đồng bộ" - "Hệ thống" "Văn bản" "Thông báo từ bên thứ ba" "Chủ đề" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index b4c993e852..71159b78e4 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -208,7 +208,6 @@ "已離開聊天室" "離開空間" "邀請被拒絕" - "深色" "解密錯誤" "描述" "開發者選項" @@ -248,7 +247,6 @@ "找不到此 Matrix ID,因此可能沒有人會收到邀請。" "正在離開聊天室" "離開空間" - "淺色" "行已複製到剪貼簿" "連結已複製到剪貼簿" "連結新裝置" @@ -361,7 +359,6 @@ "已建議" "建議" "同步中" - "系統" "文字" "第三方通知" "討論串" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 7366a4fd95..9cea16d2f0 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -215,7 +215,6 @@ "离开房间" "离开空间" "邀请已拒绝" - "深色" "解密错误" "描述" "开发者选项" @@ -255,7 +254,6 @@ "找不到此 Matrix ID,因此可能无法收到邀请。" "正在离开房间" "正在离开空间" - "浅色" "链接已复制到剪贴板" "链接已复制到剪贴板" "关联新设备" @@ -368,7 +366,6 @@ "建议" "建议" "正在同步" - "系统" "文本" "第三方通知" "消息列" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 173a525919..ee0f51000a 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -218,7 +218,6 @@ "Left room" "Left space" "Invite declined" - "Dark" "Decryption error" "Description" "Developer options" @@ -258,7 +257,6 @@ Reason: %1$s." "This Matrix ID can\'t be found, so the invite might not be received." "Leaving room" "Leaving space" - "Light" "Line copied to clipboard" "Link copied to clipboard" "Link new device" @@ -378,7 +376,6 @@ Reason: %1$s." "Suggested" "Suggestions" "Syncing" - "System" "Text" "Third-party notices" "Thread" diff --git a/libraries/ui-strings/src/main/res/values/temporary.xml b/libraries/ui-strings/src/main/res/values/temporary.xml deleted file mode 100644 index ba6c431d8b..0000000000 --- a/libraries/ui-strings/src/main/res/values/temporary.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - "Black" - diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 03ada90c4b..6ea495439e 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -331,6 +331,7 @@ "screen_blocked_users_.*", "full_screen_intent_banner_.*", "troubleshoot_notifications_entry_point_.*", + "theme\\..*", "screen\\.labs\\..*" ] }, From e8f1bf0085d4f7c7f5f2d75376ed836f6603337b Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 12 May 2026 19:33:02 +0200 Subject: [PATCH 156/471] Make Element Call screen work edge-to-edge (#6634) * Update dependency io.element.android:element-call-embedded to v0.19.3 * Remove `Scaffold` component from CallScreenView * Add immersive mode to calls in landscape orientation * Add `consumeWindowInsets`, which fixes the webview not displaying any insets for the bottom nav bar * Update screenshots * Ignore compact height in PiP mode --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: ElementBot --- .../features/call/impl/ui/CallScreenView.kt | 149 +++++++++--------- .../call/impl/ui/ElementCallActivity.kt | 25 +++ ...s.call.impl.ui_CallScreenView_Day_3_en.png | 4 +- ...call.impl.ui_CallScreenView_Night_3_en.png | 4 +- 4 files changed, 100 insertions(+), 82 deletions(-) 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 1c68a62f55..095d511e0e 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 @@ -17,9 +17,10 @@ import android.webkit.WebChromeClient import android.webkit.WebView import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBars import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -45,7 +46,6 @@ import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings import timber.log.Timber @@ -72,86 +72,79 @@ internal fun CallScreenView( } } - Scaffold( - modifier = modifier, - ) { padding -> - BackHandler { - handleBack() + BackHandler { + handleBack() + } + if (state.webViewError != null) { + ErrorDialog( + content = buildString { + append(stringResource(CommonStrings.error_unknown)) + state.webViewError.takeIf { it.isNotEmpty() }?.let { append("\n\n").append(it) } + }, + onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, + ) + } else { + var webViewAudioManager by remember { mutableStateOf(null) } + val coroutineScope = rememberCoroutineScope() + + var invalidAudioDeviceReason by remember { mutableStateOf(null) } + invalidAudioDeviceReason?.let { + InvalidAudioDeviceDialog(invalidAudioDeviceReason = it) { + invalidAudioDeviceReason = null + } } - if (state.webViewError != null) { - ErrorDialog( - content = buildString { - append(stringResource(CommonStrings.error_unknown)) - state.webViewError.takeIf { it.isNotEmpty() }?.let { append("\n\n").append(it) } - }, - onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, - ) - } else { - var webViewAudioManager by remember { mutableStateOf(null) } - val coroutineScope = rememberCoroutineScope() - var invalidAudioDeviceReason by remember { mutableStateOf(null) } - invalidAudioDeviceReason?.let { - InvalidAudioDeviceDialog(invalidAudioDeviceReason = it) { - invalidAudioDeviceReason = null - } + CallWebView( + modifier = modifier.consumeWindowInsets(WindowInsets.systemBars).fillMaxSize(), + url = state.urlState, + userAgent = state.userAgent, + onPermissionsRequest = { request -> + val androidPermissions = mapWebkitPermissions(request.resources) + val callback: RequestPermissionCallback = { request.grant(it) } + requestPermissions(androidPermissions.toTypedArray(), callback) + }, + onConsoleMessage = onConsoleMessage, + onCreateWebView = { webView -> + webView.addBackHandler(onBackPressed = ::handleBack) + val interceptor = WebViewWidgetMessageInterceptor( + webView = webView, + onUrlLoaded = { url -> + webView.evaluateJavascript("controls.onBackButtonPressed = () => { backHandler.onBackPressed() }", null) + if (webViewAudioManager?.isInCallMode?.get() == false) { + Timber.d("URL $url is loaded, starting in-call audio mode") + webViewAudioManager?.onCallStarted() + } else { + Timber.d("Can't start in-call audio mode since the app is already in it.") + } + }, + onError = { state.eventSink(CallScreenEvent.OnWebViewError(it)) }, + ) + webViewAudioManager = WebViewAudioManager( + webView = webView, + coroutineScope = coroutineScope, + onInvalidAudioDeviceAdded = { invalidAudioDeviceReason = it }, + ) + state.eventSink(CallScreenEvent.SetupMessageChannels(interceptor)) + val pipController = WebViewPipController(webView) + pipState.eventSink(PictureInPictureEvent.SetPipController(pipController)) + }, + onDestroyWebView = { + // Reset audio mode + webViewAudioManager?.onCallStopped() } - - CallWebView( - modifier = Modifier - .padding(padding) - .consumeWindowInsets(padding) - .fillMaxSize(), - url = state.urlState, - userAgent = state.userAgent, - onPermissionsRequest = { request -> - val androidPermissions = mapWebkitPermissions(request.resources) - val callback: RequestPermissionCallback = { request.grant(it) } - requestPermissions(androidPermissions.toTypedArray(), callback) - }, - onConsoleMessage = onConsoleMessage, - onCreateWebView = { webView -> - webView.addBackHandler(onBackPressed = ::handleBack) - val interceptor = WebViewWidgetMessageInterceptor( - webView = webView, - onUrlLoaded = { url -> - webView.evaluateJavascript("controls.onBackButtonPressed = () => { backHandler.onBackPressed() }", null) - if (webViewAudioManager?.isInCallMode?.get() == false) { - Timber.d("URL $url is loaded, starting in-call audio mode") - webViewAudioManager?.onCallStarted() - } else { - Timber.d("Can't start in-call audio mode since the app is already in it.") - } - }, - onError = { state.eventSink(CallScreenEvent.OnWebViewError(it)) }, - ) - webViewAudioManager = WebViewAudioManager( - webView = webView, - coroutineScope = coroutineScope, - onInvalidAudioDeviceAdded = { invalidAudioDeviceReason = it }, - ) - state.eventSink(CallScreenEvent.SetupMessageChannels(interceptor)) - val pipController = WebViewPipController(webView) - pipState.eventSink(PictureInPictureEvent.SetPipController(pipController)) - }, - onDestroyWebView = { - // Reset audio mode - webViewAudioManager?.onCallStopped() - } - ) - when (state.urlState) { - AsyncData.Uninitialized, - is AsyncData.Loading -> - ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait)) - is AsyncData.Failure -> { - Timber.e(state.urlState.error, "WebView failed to load URL: ${state.urlState.error.message}") - ErrorDialog( - content = state.urlState.error.message.orEmpty(), - onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, - ) - } - is AsyncData.Success -> Unit + ) + when (state.urlState) { + AsyncData.Uninitialized, + is AsyncData.Loading -> + ProgressDialog(text = stringResource(id = CommonStrings.common_please_wait)) + is AsyncData.Failure -> { + Timber.e(state.urlState.error, "WebView failed to load URL: ${state.urlState.error.message}") + ErrorDialog( + content = state.urlState.error.message.orEmpty(), + onSubmit = { state.eventSink(CallScreenEvent.Hangup) }, + ) } + is AsyncData.Success -> Unit } } } 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 367328ed10..26df7c160c 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 @@ -32,6 +32,9 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.content.IntentCompat import androidx.core.util.Consumer +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.Lifecycle import dev.zacsweers.metro.Inject import io.element.android.compound.colors.SemanticColorsLightDark @@ -52,6 +55,7 @@ import io.element.android.libraries.audio.api.AudioFocusRequester import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.theme.ElementThemeApp +import io.element.android.libraries.designsystem.utils.hasCompactHeightWindowSize import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.preferences.api.store.AppPreferencesStore import timber.log.Timber @@ -111,6 +115,27 @@ class ElementCallActivity : val colors by remember(webViewTarget.value?.sessionId) { enterpriseService.semanticColorsFlow(sessionId = webViewTarget.value?.sessionId) }.collectAsState(SemanticColorsLightDark.default) + + // When the height is compact, hide the system bars by default to maximize the space for the call, using immersive mode + val hasCompactHeight = hasCompactHeightWindowSize() + DisposableEffect(hasCompactHeight, pipState.isInPictureInPicture) { + if (hasCompactHeight && !pipState.isInPictureInPicture) { + val window = this@ElementCallActivity.window ?: return@DisposableEffect onDispose {} + val insetsController = WindowCompat.getInsetsController(window, window.decorView) + val systemBarInsets = WindowInsetsCompat.Type.systemBars() + insetsController.hide(systemBarInsets) + + insetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + + onDispose { + insetsController.show(systemBarInsets) + insetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT + } + } else { + onDispose {} + } + } + ElementThemeApp( appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService, diff --git a/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Day_3_en.png index b5e7419267..b45afb67bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48fa7b1415694f0a7ebb3de458edee8792f5643f681cc25627560f2e3d8ba491 -size 16331 +oid sha256:49731638f35e9c7583ece7122e3690728f3d4183a65e2e49cd270d02fb93ac70 +size 15950 diff --git a/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Night_3_en.png index 1c8ce2991d..14fd900451 100644 --- a/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.call.impl.ui_CallScreenView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebf559265b2cd4ebba7c5439b759365ed8d31532988bda5d8c76bb7c8d76dd68 -size 14892 +oid sha256:11a77776662896532ef2999e3b90aa93b3459bf9b7a5f461c06ccb3743612aab +size 14738 From 960ff0fb75000e9872b62672232c606071de0e0f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 12 May 2026 21:39:48 +0200 Subject: [PATCH 157/471] Renovate: Keep Guava on the Android variant and ignore jre-only upgrades. --- .github/renovate.json5 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 17e1fa0f1c..c16310cf43 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -34,6 +34,13 @@ "/^org.jetbrains.kotlinx:kotlinx-datetime/", ], }, + { + // Keep Guava on the Android variant and ignore jre-only upgrades. + "matchPackageNames": [ + "com.google.guava:guava", + ], + "allowedVersions": "/-android$/", + }, { // Limit PostHog Android upgrade to one PR per month, the first day of the month "matchPackageNames": [ From c959f50d53f17c0c432c1c15e65e6ea55ea7cdc2 Mon Sep 17 00:00:00 2001 From: Timo <16718859+toger5@users.noreply.github.com> Date: Wed, 13 May 2026 16:17:23 +0800 Subject: [PATCH 158/471] Back button web view to esc (revive fixed version of: https://github.com/element-hq/element-x-android/pull/6724) (#6725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change native back button behavior in EC view: - inject escape into webview instead of going back. - the webview will call back when no other modal is open. * call down and up in the webview + make sure that we fall back to close pip in case the webview did not handle the esc action. * Tests and refactor to CallScreenBackPressPolicy --------- Co-authored-by: Jorge Martín --- .../call/impl/ui/CallScreenBackPressPolicy.kt | 26 +++ .../features/call/impl/ui/CallScreenView.kt | 38 +++-- .../call/ui/CallScreenBackPressPolicyTest.kt | 96 +++++++++++ .../features/call/ui/CallScreenViewTest.kt | 151 ++++++++++++++++++ ...nticsNodeInteractionsProviderExtensions.kt | 17 ++ 5 files changed, 317 insertions(+), 11 deletions(-) create mode 100644 features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt create mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt create mode 100644 features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt new file mode 100644 index 0000000000..cd47cd8bb1 --- /dev/null +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicy.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.impl.ui +internal sealed interface CallScreenBackPressAction { + data object DispatchEscapeToWebView : CallScreenBackPressAction + data object EnterPictureInPicture : CallScreenBackPressAction +} + +internal object CallScreenBackPressPolicy { + fun resolve( + supportPip: Boolean, + hasWebView: Boolean, + fromNative: Boolean, + ): CallScreenBackPressAction? { + return when { + hasWebView && fromNative -> CallScreenBackPressAction.DispatchEscapeToWebView + hasWebView && supportPip -> CallScreenBackPressAction.EnterPictureInPicture + else -> null + } + } +} 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 095d511e0e..2537eb739c 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 @@ -64,16 +64,20 @@ internal fun CallScreenView( requestPermissions: (Array, RequestPermissionCallback) -> Unit, modifier: Modifier = Modifier, ) { - fun handleBack() { - if (pipState.supportPip) { - pipState.eventSink.invoke(PictureInPictureEvent.EnterPictureInPicture) - } else { - state.eventSink(CallScreenEvent.Hangup) + var callWebView by remember { mutableStateOf(null) } + + fun handleBack(fromNative: Boolean = false) { + when (CallScreenBackPressPolicy.resolve(supportPip = pipState.supportPip, hasWebView = callWebView != null, fromNative)) { + CallScreenBackPressAction.EnterPictureInPicture -> + pipState.eventSink(PictureInPictureEvent.EnterPictureInPicture) + CallScreenBackPressAction.DispatchEscapeToWebView -> + callWebView?.dispatchEscKeyEvent() + null -> Timber.d("Back press with unsupported pip is a no-op") } } BackHandler { - handleBack() + handleBack(fromNative = true) } if (state.webViewError != null) { ErrorDialog( @@ -105,6 +109,7 @@ internal fun CallScreenView( }, onConsoleMessage = onConsoleMessage, onCreateWebView = { webView -> + callWebView = webView webView.addBackHandler(onBackPressed = ::handleBack) val interceptor = WebViewWidgetMessageInterceptor( webView = webView, @@ -129,6 +134,7 @@ internal fun CallScreenView( pipState.eventSink(PictureInPictureEvent.SetPipController(pipController)) }, onDestroyWebView = { + callWebView = null // Reset audio mode webViewAudioManager?.onCallStopped() } @@ -241,15 +247,16 @@ private fun WebView.setup( private fun WebView.addBackHandler(onBackPressed: () -> Unit) { addJavascriptInterface( - object { - @Suppress("unused") - @JavascriptInterface - fun onBackPressed() = onBackPressed() - }, + JavascriptBackHandlerBridge(callback = onBackPressed), "backHandler" ) } +private fun WebView.dispatchEscKeyEvent() { + dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_DOWN, android.view.KeyEvent.KEYCODE_ESCAPE)) + dispatchKeyEvent(android.view.KeyEvent(android.view.KeyEvent.ACTION_UP, android.view.KeyEvent.KEYCODE_ESCAPE)) +} + @PreviewsDayNight @Composable internal fun CallScreenViewPreview( @@ -268,3 +275,12 @@ internal fun CallScreenViewPreview( internal fun InvalidAudioDeviceDialogPreview() = ElementPreview { InvalidAudioDeviceDialog(invalidAudioDeviceReason = InvalidAudioDeviceReason.BT_AUDIO_DEVICE_DISABLED) {} } + +internal class JavascriptBackHandlerBridge( + private val callback: () -> Unit, +) { + @JavascriptInterface + fun onBackPressed() { + callback() + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt new file mode 100644 index 0000000000..f07f7039d3 --- /dev/null +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.ui + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.call.impl.ui.CallScreenBackPressAction +import io.element.android.features.call.impl.ui.CallScreenBackPressPolicy +import org.junit.Test + +class CallScreenBackPressPolicyTest { + @Test + fun `resolve returns dispatch escape when a web view is available and native button is pressed`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = true, + fromNative = true, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) + } + + @Test + fun `resolve dispatch escape when there is a web view and pip is supported on native button press`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = true, + fromNative = true, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.DispatchEscapeToWebView) + } + + @Test + fun `resolve returns hangup when there is no web view and pip is not supported from native button`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = false, + fromNative = true, + ) + + assertThat(result).isNull() + } + + @Test + fun `resolve returns hangup when there is no web view even though pip is supported from native button`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = false, + fromNative = true, + ) + + assertThat(result).isNull() + } + + @Test + fun `resolve goes to pip if its not from native but from the webview`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = true, + fromNative = false, + ) + + assertThat(result).isEqualTo(CallScreenBackPressAction.EnterPictureInPicture) + } + @Test + fun `resolve hangs up if its not from native but from the webview and pip is not supported`() { + val result = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = true, + fromNative = false, + ) + + assertThat(result).isNull() + } + + @Test + fun `invalid cases (event comes from webview but there is now webview) all result in hangup`() { + val withPipSupport = CallScreenBackPressPolicy.resolve( + supportPip = true, + hasWebView = false, + fromNative = false, + ) + assertThat(withPipSupport).isNull() + val withOutPipSupport = CallScreenBackPressPolicy.resolve( + supportPip = false, + hasWebView = false, + fromNative = false, + ) + assertThat(withOutPipSupport).isNull() + } +} diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt new file mode 100644 index 0000000000..99aaee6f39 --- /dev/null +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.call.ui + +import android.view.KeyEvent +import android.webkit.WebView +import androidx.activity.ComponentActivity +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.AndroidComposeUiTest +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runAndroidComposeUiTest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.features.call.impl.pip.PictureInPictureEvent +import io.element.android.features.call.impl.pip.PictureInPictureState +import io.element.android.features.call.impl.pip.aPictureInPictureState +import io.element.android.features.call.impl.ui.CallScreenEvent +import io.element.android.features.call.impl.ui.CallScreenState +import io.element.android.features.call.impl.ui.CallScreenView +import io.element.android.features.call.impl.ui.JavascriptBackHandlerBridge +import io.element.android.features.call.impl.ui.aCallScreenState +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.pressBackKey +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.Implementation +import org.robolectric.annotation.Implements +import org.robolectric.annotation.Resetter +import org.robolectric.shadows.ShadowWebView + +@OptIn(ExperimentalTestApi::class) +@RunWith(AndroidJUnit4::class) +class CallScreenViewTest { + @Test + fun `pressing back key triggers hangup when no web view is available and pip is unsupported`() = runAndroidComposeUiTest { + val callEvents = EventsRecorder() + + setCallScreenView( + state = aCallScreenState(eventSink = callEvents), + useInspectionMode = true, + ) + + pressBackKey() + + callEvents.assertEmpty() + } + + @Config(shadows = [RecordingShadowWebView::class]) + @Test + fun `pressing back key dispatches escape key events to web view when pip is unsupported`() = runAndroidComposeUiTest { + setCallScreenView( + state = aCallScreenState(), + useInspectionMode = false, + pipState = aPictureInPictureState(supportPip = false), + ) + + pressBackKey() + + val dispatchedEvents = RecordingShadowWebView.dispatchedEvents + assertEquals(2, dispatchedEvents.size) + assertEquals(KeyEvent.ACTION_DOWN, dispatchedEvents[0].action) + assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[0].keyCode) + assertEquals(KeyEvent.ACTION_UP, dispatchedEvents[1].action) + assertEquals(KeyEvent.KEYCODE_ESCAPE, dispatchedEvents[1].keyCode) + } + + @Config(shadows = [RecordingShadowWebView::class]) + @Test + fun `web view javascript back handler emits pip event when pip is supported`() = runAndroidComposeUiTest { + val pipEvents = EventsRecorder() + + setCallScreenView( + state = aCallScreenState(), + useInspectionMode = false, + pipState = aPictureInPictureState( + supportPip = true, + eventSink = pipEvents, + ), + ) + + runOnIdle { + RecordingShadowWebView.invokeJavascriptBackHandler() + } + + pipEvents.assertSize(2) + pipEvents.assertTrue(0) { it is PictureInPictureEvent.SetPipController } + pipEvents.assertTrue(1) { it is PictureInPictureEvent.EnterPictureInPicture } + } +} + +@OptIn(ExperimentalTestApi::class) +private fun AndroidComposeUiTest.setCallScreenView( + state: CallScreenState, + useInspectionMode: Boolean, + pipState: PictureInPictureState = aPictureInPictureState(supportPip = false), +) { + setContent { + // Inspection mode disables AndroidView creation; keep it configurable per test. + CompositionLocalProvider(LocalInspectionMode provides useInspectionMode) { + CallScreenView( + state = state, + pipState = pipState, + onConsoleMessage = {}, + requestPermissions = { _, _ -> }, + ) + } + } +} + +@Implements(WebView::class) +internal class RecordingShadowWebView : ShadowWebView() { + companion object { + val dispatchedEvents = mutableListOf() + private var backHandlerJavascriptInterface: JavascriptBackHandlerBridge? = null + + @Resetter + @JvmStatic + @Suppress("unused") + fun resetRecordedEvents() { + dispatchedEvents.clear() + backHandlerJavascriptInterface = null + } + + fun invokeJavascriptBackHandler() { + val backHandler = checkNotNull(backHandlerJavascriptInterface) { "Expected backHandler JavaScript interface to be registered" } + backHandler.onBackPressed() + } + } + + @Implementation + protected override fun addJavascriptInterface(`object`: Any, name: String) { + super.addJavascriptInterface(`object`, name) + if (name == "backHandler") { + backHandlerJavascriptInterface = `object` as? JavascriptBackHandlerBridge + } + } + + @Implementation + @Suppress("unused") + fun dispatchKeyEvent(event: KeyEvent): Boolean { + dispatchedEvents += KeyEvent(event) + return false + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt index a473e6bd22..232116b385 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt @@ -23,9 +23,11 @@ import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isDialog +import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import io.element.android.libraries.ui.strings.CommonStrings +import org.junit.rules.TestRule val trueMatcher = SemanticsMatcher("true matcher") { true } @@ -48,6 +50,14 @@ fun AndroidComposeUiTest.pressBack() { onNode(hasContentDescription(text)).performClick() } +/** + * Press the back button in the app bar. + */ +fun AndroidComposeTestRule.pressBack() { + val text = activity.getString(CommonStrings.action_back) + onNode(hasContentDescription(text)).performClick() +} + /** * Press the back key. */ @@ -55,6 +65,13 @@ fun AndroidComposeUiTest.pressBackKey() { activity!!.onBackPressedDispatcher.onBackPressed() } +/** + * Press the back key. + */ +fun AndroidComposeTestRule.pressBackKey() { + activity.onBackPressedDispatcher.onBackPressed() +} + fun SemanticsNodeInteractionsProvider.pressTag(tag: String) { onNode(hasTestTag(tag)).performClick() } From 737882d35efa1975b7f0ffa1ed280be3b556204b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 10:18:59 +0200 Subject: [PATCH 159/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.13 (#6779) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5393bcc22..6a69c25787 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,7 +178,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.7" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.13" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From 31d06391ed89e415dabc30c53f015a36114771d6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 13 May 2026 11:39:46 +0200 Subject: [PATCH 160/471] Pin code: remove the key if there is no pin code --- .../lockscreen/impl/pin/DefaultPinCodeManager.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt index 2bda5759a8..d699357933 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/pin/DefaultPinCodeManager.kt @@ -16,6 +16,10 @@ import io.element.android.libraries.cryptography.api.EncryptionDecryptionService import io.element.android.libraries.cryptography.api.EncryptionResult import io.element.android.libraries.cryptography.api.SecretKeyRepository import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import java.util.concurrent.CopyOnWriteArrayList internal const val SECRET_KEY_ALIAS = "elementx.SECRET_KEY_ALIAS_PIN_CODE" @@ -29,6 +33,8 @@ class DefaultPinCodeManager( ) : PinCodeManager { private val callbacks = CopyOnWriteArrayList() + private val migrationMutex = Mutex() + override fun addCallback(callback: PinCodeManager.Callback) { callbacks.add(callback) } @@ -39,6 +45,15 @@ class DefaultPinCodeManager( override fun hasPinCode(): Flow { return secretKeyRepository.hasKey(SECRET_KEY_ALIAS) + .onStart { + migrationMutex.withLock { + val hasKey = secretKeyRepository.hasKey(SECRET_KEY_ALIAS).first() + if (hasKey && lockScreenStore.getEncryptedCode() == null) { + // Remove the key if there is no pin code + secretKeyRepository.deleteKey(SECRET_KEY_ALIAS) + } + } + } } override suspend fun getPinCodeSize(): Int? { From 7e6d3c60d2df7f1f9dd87638295392f5aaed66f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 13 May 2026 12:15:26 +0200 Subject: [PATCH 161/471] Setting version for the release 26.05.1 --- enterprise | 2 +- plugins/src/main/kotlin/Versions.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/enterprise b/enterprise index fb7e9287d9..6781da90aa 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit fb7e9287d9d446012925139842d9aaa8e99a74dc +Subproject commit 6781da90aae61cf77dcdbc543e18d76411d578b4 diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index fa197e4a08..a3de4327b4 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 5 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 0 +private const val versionReleaseNumber = 1 object Versions { /** From 4823267013eda7feb9406de18938cc236d1aa371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 13 May 2026 13:12:42 +0200 Subject: [PATCH 162/471] Adding fastlane file for version 26.05.1 --- fastlane/metadata/android/en-US/changelogs/202605010.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202605010.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202605010.txt b/fastlane/metadata/android/en-US/changelogs/202605010.txt new file mode 100644 index 0000000000..0ad08f5b4d --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202605010.txt @@ -0,0 +1,2 @@ +Main changes in this version: improvements in Element Call, room knocking and room directory are now available, improvements on DMs. +Full changelog: https://github.com/element-hq/element-x-android/releases From 5e577e2360f154e56fe4c0520c6a33fbd6ac705e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 13 May 2026 13:59:48 +0200 Subject: [PATCH 163/471] Changelog for version 26.05.1 --- CHANGES.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a57395b833..19665d542c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,47 @@ +Changes in Element X v26.05.1 +============================= + + + +## What's Changed +### ✨ Features +* Make Element Call screen work edge-to-edge by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6634 +### 🙌 Improvements +* Stop removing the `logs` dir when clearing cache by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6765 +* Adapt to new DM definition changes in the SDK by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6748 +* feat: Update call started timeline item + declined support by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/6649 +### 🐛 Bugfixes +* Improve pin code UX by @bmarty in https://github.com/element-hq/element-x-android/pull/6744 +* Use just the other user's avatar for DM details by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6738 +* Improve `FetchPushForegroundService`'s reliability by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6757 +* Prevent user from starting Live Location Sharing in thread by @bmarty in https://github.com/element-hq/element-x-android/pull/6767 +* Fix media playback from the timeline broken when exiting a thread by @bmarty in https://github.com/element-hq/element-x-android/pull/6771 +* Pin code: remove the key if there is no pin code by @bmarty in https://github.com/element-hq/element-x-android/pull/6780 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6761 +### 🚧 In development 🚧 +* Feature : share live location by @ganfra in https://github.com/element-hq/element-x-android/pull/6741 +### Dependency upgrades +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6746 +* Update actions/add-to-project action to v2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6758 +* Update dependency io.github.sergio-sastre.ComposablePreviewScanner:android to v0.9.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6759 +* Update dependency io.element.android:element-call-embedded to v0.19.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6766 +* Update metro to v1 (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6720 +* Update tspascoal/get-user-teams-membership action to v4.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6750 +* Update plugin sonarqube to v7.3.0.8198 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6743 +* Update plugin dependencycheck to v12.2.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6760 +* Update dependency com.google.guava:guava to v33.6.0-android by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6646 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6779 +### Others +* Render media captions formatting in the media viewer by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6729 +* Reduce FeatureFlag `Knock` effect on room creation and room edition forms by @bmarty in https://github.com/element-hq/element-x-android/pull/6768 +* Use the right analytics span as a parent in `checkNetworkConnection` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6751 +* Add missing strings `theme.black` by @bmarty in https://github.com/element-hq/element-x-android/pull/6772 +* Map back button in web view to esc (revive fixed version of: https://github.com/element-hq/element-x-android/pull/6724) by @toger5 in https://github.com/element-hq/element-x-android/pull/6725 + + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.05.0...v26.05.1 + Changes in Element X v26.05.0 ============================= From e563fc79199ea93481c183c85de5d4e833131824 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 08:08:21 +0200 Subject: [PATCH 164/471] Update dependency androidx.compose:compose-bom to v2026.05.00 (#6784) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a69c25787..a786c342e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ camera = "1.6.0" work = "2.11.2" # Compose -compose_bom = "2026.04.01" +compose_bom = "2026.05.00" # Coroutines coroutines = "1.10.2" From 486754602dfd164ea2a00fe40061fedea5587588 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 08:09:00 +0200 Subject: [PATCH 165/471] Update dependency io.sentry:sentry-android to v8.41.0 (#6787) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a786c342e4..5eac4e71a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -222,7 +222,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics posthog = "com.posthog:posthog-android:3.43.0" -sentry = "io.sentry:sentry-android:8.40.0" +sentry = "io.sentry:sentry-android:8.41.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.33.2" From 432a7712c43aca40ffa1d64d90448e6e48f5fef3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 09:03:50 +0200 Subject: [PATCH 166/471] Update kotlin (#6790) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5eac4e71a4..a8158a638f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ work = "2.11.2" compose_bom = "2026.05.00" # Coroutines -coroutines = "1.10.2" +coroutines = "1.11.0" # Accompanist accompanist = "0.37.3" @@ -35,7 +35,7 @@ test_core = "1.7.0" roborazzi = "1.60.0" # Jetbrain -datetime = "0.7.1" +datetime = "0.8.0" serialization_json = "1.11.0" #other From cbc677b80dd11f7d634e86f5463f288fed3ba343 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Fri, 15 May 2026 01:43:21 -0700 Subject: [PATCH 167/471] Fix room list duplicate-detection telemetry crashing before it can report (#6791) * Add room dupe regression tests * Fix telemetry path for dedupe discovery --- .../impl/datasource/RoomListDataSourceTest.kt | 100 +++++++++++++++++- .../impl/roomlist/RoomListEntriesUpdateExt.kt | 12 +-- .../impl/roomlist/RoomSummaryListProcessor.kt | 7 +- .../roomlist/RoomSummaryListProcessorTest.kt | 56 +++++++++- 4 files changed, 165 insertions(+), 10 deletions(-) diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt index 1ce5061356..ace1ef2eaa 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt @@ -14,6 +14,8 @@ import io.element.android.features.home.impl.FakeDateTimeObserver import io.element.android.libraries.androidutils.system.DateTimeObserver import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.roomlist.FakeDynamicRoomList @@ -100,11 +102,107 @@ class RoomListDataSourceTest { } } + /** + * Tracking issue #4182: rooms duplicated in the room list around midnight. + * + * If the SDK ever leaks a list containing the same roomId twice (the suspected cause of #4182), + * the UI mapper's `distinctBy` safety net in [RoomListDataSource.buildAndEmitAllRooms] must + * remove the duplicate AND `analyticsService.trackError` must fire so the team can root-cause + * it via Sentry. + */ + @Test + fun `when SDK summaries source contains duplicate roomIds, UI layer dedupes and reports trackError`() = runTest { + val analyticsService = FakeAnalyticsService() + val duplicatedSummaries = listOf( + aRoomSummary(roomId = A_ROOM_ID), + aRoomSummary(roomId = A_ROOM_ID), + aRoomSummary(roomId = A_ROOM_ID_2), + ) + val roomList = FakeDynamicRoomList(summaries = MutableStateFlow(duplicatedSummaries)) + val roomListService = FakeRoomListService( + createRoomListLambda = { roomList } + ).apply { + postState(RoomListService.State.Running) + } + val roomListDataSource = createRoomListDataSource( + roomListService = roomListService, + analyticsService = analyticsService, + ) + + roomListDataSource.roomSummariesFlow.test { + roomListDataSource.launchIn(backgroundScope) + val list = awaitItem() + assertThat(list.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + assertThat(analyticsService.trackedErrors).hasSize(1) + } + } + + /** + * Tracking issue #4182. + * + * Targeted scenario: a `DateChanged` tick fires after an initial SDK emit, then a follow-up + * SDK emit lands (mimicking "midnight, then a new message arrives"). Even though the diffCache + * is bypassed during the rebuild (`useCache = false`), the final state must contain each + * roomId exactly once and trackError must not fire on a happy path. + */ + @Test + fun `interleaved date change and SDK update with overlapping content does not produce duplicates`() = runTest { + val analyticsService = FakeAnalyticsService() + val summariesFlow = MutableStateFlow( + listOf( + aRoomSummary(roomId = A_ROOM_ID), + aRoomSummary(roomId = A_ROOM_ID_2), + ) + ) + val roomList = FakeDynamicRoomList(summaries = summariesFlow) + val roomListService = FakeRoomListService( + createRoomListLambda = { roomList } + ).apply { + postState(RoomListService.State.Running) + } + val dateTimeObserver = FakeDateTimeObserver() + val roomListDataSource = createRoomListDataSource( + roomListService = roomListService, + dateTimeObserver = dateTimeObserver, + analyticsService = analyticsService, + ) + + roomListDataSource.roomSummariesFlow.test { + roomListDataSource.launchIn(backgroundScope) + val initial = awaitItem() + assertThat(initial.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + + // Midnight ticks while the cache holds [A_ROOM_ID, A_ROOM_ID_2] + dateTimeObserver.given(DateTimeObserver.Event.DateChanged(Instant.MIN, Instant.now())) + val afterMidnight = awaitItem() + assertThat(afterMidnight.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + + // A new message bumps A_ROOM_ID — different unread count makes the StateFlow see this + // as a new value + summariesFlow.value = listOf( + aRoomSummary(roomId = A_ROOM_ID, numUnreadMessages = 1), + aRoomSummary(roomId = A_ROOM_ID_2), + ) + val afterMessage = awaitItem() + assertThat(afterMessage.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + assertThat(afterMessage.map { it.roomId }.toSet()).hasSize(afterMessage.size) + + // Second midnight rebuild after the new message + dateTimeObserver.given(DateTimeObserver.Event.DateChanged(Instant.MIN, Instant.now())) + val afterSecondMidnight = awaitItem() + assertThat(afterSecondMidnight.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + assertThat(afterSecondMidnight.map { it.roomId }.toSet()).hasSize(afterSecondMidnight.size) + + assertThat(analyticsService.trackedErrors).isEmpty() + } + } + private fun TestScope.createRoomListDataSource( roomListService: FakeRoomListService = FakeRoomListService(), roomListRoomSummaryFactory: RoomListRoomSummaryFactory = aRoomListRoomSummaryFactory(), notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), dateTimeObserver: FakeDateTimeObserver = FakeDateTimeObserver(), + analyticsService: FakeAnalyticsService = FakeAnalyticsService(), ) = RoomListDataSource( roomListService = roomListService, roomListRoomSummaryFactory = roomListRoomSummaryFactory, @@ -112,6 +210,6 @@ class RoomListDataSourceTest { notificationSettingsService = notificationSettingsService, sessionCoroutineScope = backgroundScope, dateTimeObserver = dateTimeObserver, - analyticsService = FakeAnalyticsService(), + analyticsService = analyticsService, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListEntriesUpdateExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListEntriesUpdateExt.kt index 27b19ebf19..5d8367bb99 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListEntriesUpdateExt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListEntriesUpdateExt.kt @@ -16,25 +16,25 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate internal fun RoomListEntriesUpdate.describe(): String { return when (this) { is RoomListEntriesUpdate.Set -> { - "Set #$index to '${value.displayName()}'" + "Set #$index to '${value.id()}'" } is RoomListEntriesUpdate.Append -> { - "Append ${values.map { "'" + it.displayName() + "'" }}" + "Append ${values.map { "'" + it.id() + "'" }}" } is RoomListEntriesUpdate.PushBack -> { - "PushBack '${value.displayName()}'" + "PushBack '${value.id()}'" } is RoomListEntriesUpdate.PushFront -> { - "PushFront '${value.displayName()}'" + "PushFront '${value.id()}'" } is RoomListEntriesUpdate.Insert -> { - "Insert at #$index: '${value.displayName()}'" + "Insert at #$index: '${value.id()}'" } is RoomListEntriesUpdate.Remove -> { "Remove #$index" } is RoomListEntriesUpdate.Reset -> { - "Reset all to ${values.map { "'" + it.displayName() + "'" }}" + "Reset all to ${values.map { "'" + it.id() + "'" }}" } RoomListEntriesUpdate.PopBack -> { "PopBack" diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt index 968a768fa2..afdac3db12 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt @@ -112,6 +112,11 @@ class RoomSummaryListProcessor( private suspend fun updateRoomSummaries(updates: List, block: suspend MutableList.() -> Unit) = withContext( coroutineContext ) { + // Capture the description before applying updates: applyUpdate consumes each Room via + // `entry.use { ... }` which destroys it, and the duplicate-detection branch below reads + // id() through `describe()`. Without this capture the trackError call crashes before it + // can be reported. + val updatesDescription = updates.description() mutex.withLock { val current = roomSummaries.replayCache.lastOrNull() val mutableRoomSummaries = current.orEmpty().toMutableList() @@ -126,7 +131,7 @@ class RoomSummaryListProcessor( analyticsService.trackError( IllegalStateException( "Found duplicates in room summaries after a list update from the SDK: $duplicates. " + - "Updates: ${updates.description()}" + "Updates: $updatesDescription" ) ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt index 6fac54b904..d951b3d339 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessorTest.kt @@ -173,16 +173,68 @@ class RoomSummaryListProcessorTest { assertThat(summaries.value[index].roomId).isEqualTo(A_ROOM_ID_3) } + /** + * Tracking issue #4182 / #5031: rooms duplicated in the room list. + * + * If duplicates are present in the upstream summaries flow, the dedupe safety net in + * [RoomSummaryListProcessor.updateRoomSummaries] must remove them and report the incident via + * [analyticsService.trackError]. Uses an empty update to drive the dedupe path without + * passing a Rust Room through the destroy-on-use path. + */ + @Test + fun `pre-existing duplicates in summaries are deduped on next update and trackError fires`() = runTest { + summaries.value = listOf( + aRoomSummary(roomId = A_ROOM_ID), + aRoomSummary(roomId = A_ROOM_ID), // simulated SDK-side leak + aRoomSummary(roomId = A_ROOM_ID_2), + ) + val analyticsService = FakeAnalyticsService() + val processor = createProcessor(analyticsService = analyticsService) + + processor.postUpdate(emptyList()) + + assertThat(summaries.value.map { it.roomId }).containsExactly(A_ROOM_ID, A_ROOM_ID_2).inOrder() + assertThat(analyticsService.trackedErrors).hasSize(1) + } + + /** + * Tracking issue #4182 / #5031. + * + * Insert is the most likely Rust-SDK trigger for a duplicate-room report: it blindly inserts + * a new entry at an index without checking whether the roomId already exists. Before the + * describe-capture fix, the dedupe branch in [updateRoomSummaries] would call `Room.id()` + * on an already-destroyed Room (because [applyUpdate] consumes each value via + * `entry.use { ... }`) and crash before [trackError] could be invoked. This test guards the + * fix: the Insert is processed, the list is emitted deduplicated, and the tracked error + * message carries the human-readable description of the offending update. + */ + @Test + fun `Insert that triggers dedupe is reported via trackError without crashing`() = runTest { + summaries.value = listOf(aRoomSummary(roomId = A_ROOM_ID)) + val analyticsService = FakeAnalyticsService() + val processor = createProcessor(analyticsService = analyticsService) + + processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(0u, aRustRoom(A_ROOM_ID)))) + + assertThat(summaries.value.map { it.roomId }).containsExactly(A_ROOM_ID) + assertThat(analyticsService.trackedErrors).hasSize(1) + val message = analyticsService.trackedErrors.single().message.orEmpty() + assertThat(message).contains("Found duplicates") + assertThat(message).contains("Insert at #0") + } + private fun aRustRoom(roomId: RoomId = A_ROOM_ID) = FakeFfiRoom( roomId = roomId, latestEventLambda = { LatestEventValue.None } ) - private fun TestScope.createProcessor() = RoomSummaryListProcessor( + private fun TestScope.createProcessor( + analyticsService: FakeAnalyticsService = FakeAnalyticsService(), + ) = RoomSummaryListProcessor( summaries, FakeFfiRoomListService(), coroutineContext = StandardTestDispatcher(testScheduler), roomSummaryFactory = RoomSummaryFactory(), - analyticsService = FakeAnalyticsService(), + analyticsService = analyticsService, ) } From dcc67f9fc6b1bb4a3d3ea14d716a2386cb21b08b Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 18 May 2026 00:57:56 +0000 Subject: [PATCH 168/471] Sync Strings from Localazy --- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 20 + .../src/main/res/values-cs/translations.xml | 6 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 6 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 4 +- .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 4 + .../src/main/res/values-ro/translations.xml | 4 + .../src/main/res/values-ro/translations.xml | 3 + .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 2 + .../src/main/res/values-ro/translations.xml | 6 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 8 +- .../src/main/res/values-ro/translations.xml | 13 +- .../src/main/res/values-et/translations.xml | 16 +- .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 4 + .../src/main/res/values-hu/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 10 + .../src/main/res/values-zh/translations.xml | 3 +- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 4 +- .../src/main/res/values-ro/translations.xml | 14 +- .../src/main/res/values-et/translations.xml | 27 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 11 +- .../src/main/res/values-ro/translations.xml | 11 + .../src/main/res/values-et/translations.xml | 8 +- .../src/main/res/values-ro/translations.xml | 4 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 2 + .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 3 +- .../src/main/res/values-ro/translations.xml | 8 +- .../src/main/res/values-cs/translations.xml | 8 + .../src/main/res/values-et/translations.xml | 35 +- .../src/main/res/values-fi/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 109 +- ...s.call.impl.ui_CallScreenView_Day_3_de.png | 4 +- ...api_LiveLocationSharingBanner_Day_0_de.png | 3 + ....impl.share_ShareLocationView_Day_6_de.png | 4 +- ....impl.share_ShareLocationView_Day_7_de.png | 3 + ....impl.share_ShareLocationView_Day_8_de.png | 3 + ...vent_TimelineItemLocationView_Day_1_de.png | 4 +- ...vent_TimelineItemLocationView_Day_2_de.png | 4 +- ...vent_TimelineItemLocationView_Day_3_de.png | 4 +- ...ts_TimelineItemCallNotifyView_Day_0_de.png | 4 +- ...s.messages.impl_MessagesView_Day_10_de.png | 4 +- ...s.messages.impl_MessagesView_Day_11_de.png | 3 + ...es.messages.impl_MessagesView_Day_8_de.png | 4 +- ...es.messages.impl_MessagesView_Day_9_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_0_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_1_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_2_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_3_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_4_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_5_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_6_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_7_de.png | 4 +- ...dvanced_AdvancedSettingsViewBlack_8_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_0_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_1_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_2_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_3_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_4_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_5_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_6_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_7_de.png | 4 +- ...advanced_AdvancedSettingsViewDark_8_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_0_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_1_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_2_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_3_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_4_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_5_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_6_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_7_de.png | 4 +- ...dvanced_AdvancedSettingsViewLight_8_de.png | 4 +- screenshots/html/data.js | 2128 +++++++++-------- 96 files changed, 1447 insertions(+), 1231 deletions(-) create mode 100644 features/location/impl/src/main/res/values-ro/translations.xml create mode 100644 screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png create mode 100644 screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png create mode 100644 screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png create mode 100644 screenshots/de/features.messages.impl_MessagesView_Day_11_de.png diff --git a/features/call/impl/src/main/res/values-et/translations.xml b/features/call/impl/src/main/res/values-et/translations.xml index 16b72b8b97..fa0415a5f5 100644 --- a/features/call/impl/src/main/res/values-et/translations.xml +++ b/features/call/impl/src/main/res/values-et/translations.xml @@ -4,5 +4,5 @@ "Kõne juurde naasmiseks klõpsa" "☎️ Kõne on pooleli" "Element Call ei võimalda selles Androidi versioonis Bluetoothi heliseadmete kasutamist. Palun vali mõni muu heliseade." - "Sissetulev Element Calli kõne" + "Saabuv Element Calli kõne" diff --git a/features/createroom/impl/src/main/res/values-ro/translations.xml b/features/createroom/impl/src/main/res/values-ro/translations.xml index fd1854db40..33a5351877 100644 --- a/features/createroom/impl/src/main/res/values-ro/translations.xml +++ b/features/createroom/impl/src/main/res/values-ro/translations.xml @@ -3,14 +3,34 @@ "Cameră nouă" "Invitați prieteni" "A apărut o eroare la crearea camerei" + "Spațiul nu a putut fi creat din cauza unei erori necunoscute. Încercați din nou mai târziu." + "Adăugați un nume…" + "Cameră nouă" + "Spațiu nou" "Doar persoanele invitate se pot alătura." + "Privat" "Oricine poate găsi această cameră. Puteți modifica acest lucru oricând în setări." + "Oricine se poate alătura." + "Public" "Oricine poate cere să se alăture camerei, dar un administrator sau un moderator va trebui să accepte cererea" "Permite solicitarea de alăturare" + "Oricine din %1$s se poate alătura, dar oricine altcineva trebuie să solicite acces." + "Solicitați să vă alăturați" + "Doar persoanele invitate se pot alătura." + "Privat" "Oricine se poate alătura acestei camere" + "Public" + "Oricine din %1$s se poate alătura." + "Standard" + "Cine are acces" "Pentru ca această cameră să fie vizibilă în directorul de camere publice, veți avea nevoie de o adresă de cameră." "Adresă" "Vizibilitatea camerei" + "(nicun spațiu)" + "Nu adăugați la un spațiu" + "Niciun spațiu selectat" + "Adăugați la spațiu" "Subiect (opțional)" + "Adăugați o descriere…" diff --git a/features/deactivation/impl/src/main/res/values-cs/translations.xml b/features/deactivation/impl/src/main/res/values-cs/translations.xml index e0f4fd14c7..13659e1a75 100644 --- a/features/deactivation/impl/src/main/res/values-cs/translations.xml +++ b/features/deactivation/impl/src/main/res/values-cs/translations.xml @@ -1,14 +1,14 @@ - "Potvrďte prosím, že chcete svůj účet deaktivovat. Tuto akci nelze vrátit zpět." + "Potvrďte prosím, že chcete smazat svůj účet. Tuto akci nelze vrátit zpět." "Smazat všechny mé zprávy" "Upozornění: Budoucí uživatelé mohou vidět neúplné konverzace." - "Deaktivace vašeho účtu je %1$s, což způsobí:" + "Smazání účtu je %1$s, dojde k:" "nezvratná" "%1$s váš účet (nemůžete se znovu přihlásit a vaše ID nelze znovu použít)." "Trvale zakázat" "Odebere vás ze všech chatovacích místností." "Odstraní informace o vašem účtu z našeho serveru identit." "Vaše zprávy budou stále viditelné registrovaným uživatelům, ale nebudou dostupné novým ani neregistrovaným uživatelům, pokud se rozhodnete je smazat." - "Deaktivovat účet" + "Smazat účet" diff --git a/features/deactivation/impl/src/main/res/values-et/translations.xml b/features/deactivation/impl/src/main/res/values-et/translations.xml index 1776e2de17..bfb41d7665 100644 --- a/features/deactivation/impl/src/main/res/values-et/translations.xml +++ b/features/deactivation/impl/src/main/res/values-et/translations.xml @@ -3,7 +3,7 @@ "Palun kinnita uuesti, et soovid kustutada oma kasutajakonto. Seda tegevust ei saa tagasi pöörata." "Kustuta kõik minu sõnumid" "Hoiatus: tulevased kasutajad võivad näha poolikuid vestlusi." - "Sinu konto kasutusest eemaldamine on %1$s ja sellega:" + "Sinu konto kustutamine on %1$s ja sellega:" "pöördumatu" "Sinu kasutajakonto %1$s (sa ei saa enam sellega võrku logida ning kasutajatunnust ei saa enam pruukida)." "jäädavalt eemaldatakse kasutusest" diff --git a/features/deactivation/impl/src/main/res/values-ro/translations.xml b/features/deactivation/impl/src/main/res/values-ro/translations.xml index acd4c0747d..6176b4584e 100644 --- a/features/deactivation/impl/src/main/res/values-ro/translations.xml +++ b/features/deactivation/impl/src/main/res/values-ro/translations.xml @@ -1,14 +1,14 @@ - "Vă rugăm să confirmați că doriți să vă dezactivați contul. Această acțiune nu poate fi anulată." + "Vă rugăm să confirmați că doriți să vă ștergeți contul. Această acțiune nu poate fi anulată." "Ștergeți toate mesajele mele" "Avertisment: este posibil ca viitorii utilizatori să vadă conversații incomplete." - "Dezactivarea contului dumneavoastră este %1$s, acesta va:" + "Ștergerea contului dumneavoastră este %1$s, acesta va:" "ireversibilă" "%1$s contul dumneavoastră (nu vă puteți conecta din nou, iar ID-ul dvs. nu poate fi reutilizat)." "Dezactivați permanent" "Îndepărta din toate camerele de chat." "Șterge informațiile contului dumneavoastră de pe serverul nostru de identitate." "Mesajele dumneavoastră vor fi în continuare vizibile pentru utilizatorii înregistrați, dar nu vor fi disponibile pentru utilizatorii noi sau neînregistrați dacă alegeți să le ștergeți." - "Dezactivați contul" + "Ștergeți contul" diff --git a/features/ftue/impl/src/main/res/values-et/translations.xml b/features/ftue/impl/src/main/res/values-et/translations.xml index 3e961d6249..c013556568 100644 --- a/features/ftue/impl/src/main/res/values-et/translations.xml +++ b/features/ftue/impl/src/main/res/values-et/translations.xml @@ -2,7 +2,7 @@ "Kas kinnitamine pole võimalik?" "Loo uus taastevõti" - "Krüptitud sõnumivahetuse tagamiseks verifitseeri see seade." + "Turvalise sõnumside seadistamiseks vali verifitseerimise viis." "Kinnita oma digitaalne identiteet" "Kasuta teist seadet" "Kasuta taastevõtit" diff --git a/features/home/impl/src/main/res/values-et/translations.xml b/features/home/impl/src/main/res/values-et/translations.xml index 9d938ed941..4ba1695d7e 100644 --- a/features/home/impl/src/main/res/values-et/translations.xml +++ b/features/home/impl/src/main/res/values-et/translations.xml @@ -5,8 +5,8 @@ "Sa ei näe kõiki teavitusi?" "Sinu nutiseadme teavituste heli on uuenenud - see on nüüd selgem, kiirem ja vähem häiriv." "Oleme sinu helisid värskendanud" - "Loo uus taastevõti, mida saad kasutada oma krüptitud sõnumite ajaloo taastamisel olukorras, kus kaotad ligipääsu oma seadmetele." - "Seadista andmete taastamine" + "Sinu vestlused on automaatselt varundatud kasutades läbivat krüptimist. Kui peaksid kaotama ligipääsu kõikidele oma seadmetele, siis selle varukoopia taastamiseks ja oma digitaalse identiteedi säilitamiseks, on vaja taastevõtit." + "Seadista taastevõti" "Varunda oma vestlused" "Säilitamaks ligipääsu vestluste ja krüptovõtmete varukoopiale, palun sisesta kinnituseks oma taastevõti." "Sisesta oma taastevõti" diff --git a/features/home/impl/src/main/res/values-ro/translations.xml b/features/home/impl/src/main/res/values-ro/translations.xml index be1933e160..14cf100c22 100644 --- a/features/home/impl/src/main/res/values-ro/translations.xml +++ b/features/home/impl/src/main/res/values-ro/translations.xml @@ -50,6 +50,7 @@ Nu aveți mesaje necitite!" "Marcați ca citită" "Marcați ca necitită" "Această cameră a fost modernizată." + "Spațiile dumneavoastră" "Se pare că folosiți un dispozitiv nou. Verificați-vă identitatea cu un alt dispozitiv pentru a accesa mesajele dumneavoastră criptate." "Verificați că sunteți dumneavoastră" diff --git a/features/invitepeople/impl/src/main/res/values-et/translations.xml b/features/invitepeople/impl/src/main/res/values-et/translations.xml index 44484d23c3..1bdf5f780c 100644 --- a/features/invitepeople/impl/src/main/res/values-et/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-et/translations.xml @@ -2,4 +2,8 @@ "Sa juba oled jututoa liige" "Sa juba oled kutse saanud" + "Sul pole hetkel nende kontaktidega ühtegi vestlust. Enne jätkamist kinnita neile siia jututuppa kutse saatmine." + "Sul pole hetkel selle kontaktiga ühtegi vestlust. Enne jätkamist kinnita talle siia jututuppa kutse saatmine." + "Kas kutsud uued kontaktid siia jututuppa?" + "Kas kutsud uue kontakti siia jututuppa?" diff --git a/features/invitepeople/impl/src/main/res/values-ro/translations.xml b/features/invitepeople/impl/src/main/res/values-ro/translations.xml index f03be4b263..40189e3186 100644 --- a/features/invitepeople/impl/src/main/res/values-ro/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-ro/translations.xml @@ -2,4 +2,8 @@ "Deja membru" "Deja invitat" + "În prezent, nu aveți nicio conversație cu aceste contacte. Confirmați invitarea lor în această cameră înainte de a continua." + "În prezent, nu aveți nicio conversație cu acest contact. Confirmați invitarea acestuia în cameră înainte de a continua." + "Invitați contactele noi în această cameră?" + "Invitați contactul nou în această cameră?" diff --git a/features/linknewdevice/impl/src/main/res/values-ro/translations.xml b/features/linknewdevice/impl/src/main/res/values-ro/translations.xml index c99b537084..57d439240d 100644 --- a/features/linknewdevice/impl/src/main/res/values-ro/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-ro/translations.xml @@ -26,6 +26,7 @@ "Se încarcă codul QR…" "Dispozitiv mobil" "Ce tip de dispozitiv doriți să conectați?" + "Încercați din nou și asigurați-vă că ați introdus corect codul de 2 cifre. Dacă numerele tot nu se potrivesc, contactați furnizorul contului." "Numerele nu se potrivesc" "Nu a putut fi făcută o conexiune sigură la noul dispozitiv. Dispozitivele existente sunt încă în siguranță și nu trebuie să vă faceți griji cu privire la ele." "Și acum?" @@ -39,6 +40,8 @@ "Cererea de autentificare a fost anulată" "Autentificarea a fost refuzată pe celălalt dispozitiv." "Autentificarea a fost refuzată" + "Nu trebuie să faceți nimic altceva." + "Celălalt dispozitiv este deja conectat" "Autentificarea a expirat. Vă rugăm să încercați din nou." "Autentificarea nu a fost finalizată la timp" "Celălalt dispozitiv nu acceptă autentificarea la %s cu un cod QR. diff --git a/features/location/impl/src/main/res/values-cs/translations.xml b/features/location/impl/src/main/res/values-cs/translations.xml index 6bb5b1db8c..98af57abba 100644 --- a/features/location/impl/src/main/res/values-cs/translations.xml +++ b/features/location/impl/src/main/res/values-cs/translations.xml @@ -2,4 +2,5 @@ "Vaše historie aktuální polohy bude uložena v místnosti a bude viditelná pro členy i po skončení relace." "Zvolte, jak dlouho chcete sdílet svou aktuální polohu." + "Nemáte oprávnění sdílet svou aktuální polohu v této místnosti." diff --git a/features/location/impl/src/main/res/values-et/translations.xml b/features/location/impl/src/main/res/values-et/translations.xml index 53aede1bb2..73847bcb43 100644 --- a/features/location/impl/src/main/res/values-et/translations.xml +++ b/features/location/impl/src/main/res/values-et/translations.xml @@ -1,4 +1,6 @@ + "Sinu reaalajas jagatud asukoha ajalugu salvestub siin jututoas ja see on liikmetele nähtav ka pärast jagamissessiooni lõppu." "Vali, kui kaua tahad oma reaalajas jagada." + "Sul pole õigust jagada selles jututoas oma asukohta reaalajas" diff --git a/features/location/impl/src/main/res/values-ro/translations.xml b/features/location/impl/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..85e665647a --- /dev/null +++ b/features/location/impl/src/main/res/values-ro/translations.xml @@ -0,0 +1,6 @@ + + + "Istoricul locațiilor dumneavoastră va fi stocat în cameră și va fi vizibil pentru membri după încheierea sesiunii." + "Alegeți cât timp doriți să vă partajați locația în timp real." + "Nu aveți permisiunea de a vă partaja locația în această cameră." + diff --git a/features/location/impl/src/main/res/values-zh/translations.xml b/features/location/impl/src/main/res/values-zh/translations.xml index 563bba593f..86f2f2a9da 100644 --- a/features/location/impl/src/main/res/values-zh/translations.xml +++ b/features/location/impl/src/main/res/values-zh/translations.xml @@ -1,6 +1,6 @@ - "你实时位置历史将存储在房间中,并于会话结束后对其他成员可见。" + "你的实时位置历史将存储在房间中,并于会话结束后对其他成员可见。" "选择共享实时位置的时长。" "你无权在此房内共享实时位置。" diff --git a/features/lockscreen/impl/src/main/res/values-et/translations.xml b/features/lockscreen/impl/src/main/res/values-et/translations.xml index ac25914f8c..7137c09b60 100644 --- a/features/lockscreen/impl/src/main/res/values-et/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-et/translations.xml @@ -34,5 +34,5 @@ Vali midagi, mis hästi meelde jääb. Kui unustad selle PIN-koodi, siis turvaka "Kasuta biomeetriat" "Kasuta PIN-koodi" - "Logime välja…" + "Eemaldan seadet…" diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml index c5887de6c6..3181e823bb 100644 --- a/features/login/impl/src/main/res/values-cs/translations.xml +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -28,7 +28,7 @@ "Jaká je adresa vašeho serveru?" "Vyberte váš server" "Vytvořit účet" - "Tento účet byl deaktivován." + "Tento účet byl smazán." "Nesprávné uživatelské jméno nebo heslo" "Toto není platný identifikátor uživatele. Očekávaný formát: \'@user:homeserver.org\'" "Tento server je nakonfigurován tak, aby používal obnovovací tokeny. Ty nejsou podporovány při použití přihlašovacích údajů založených na hesle." diff --git a/features/login/impl/src/main/res/values-et/translations.xml b/features/login/impl/src/main/res/values-et/translations.xml index b0b60009d4..dc37ed3e38 100644 --- a/features/login/impl/src/main/res/values-et/translations.xml +++ b/features/login/impl/src/main/res/values-et/translations.xml @@ -28,7 +28,7 @@ "Mis on sinu koduserveri aadress?" "Vali oma server" "Loo kasutajakonto" - "Konto on kasutusest eemaldatud." + "See kasutajakonto on kustutatud." "Vigane kasutajanimi ja/või salasõna" "See ei ole korrektne kasutajanimi. Õige vorming on: „@kasutaja:koduserver.ee“" "See server on seadistatud kasutama tunnusloa põhist sisselogimist. Salasõnaga sisselogimisel see võimalus aga ei ole toetatud." @@ -39,7 +39,13 @@ "Logi sisse serverisse %1$s" "Ava Element Classic" "Ava Element Classic oma seadmes" + "Ava „Seadistused“ → „Turvalisus ja privaatsus“" + "Krüptovõtmete halduses vali „Krüptitud sõnumite taastamine“" + "Võtmehoidla kasutuselevõtmiseks palun järgi juhendit" + "Tule tagasi rakendusse %1$s" + "Enne jätkamist rakenduses %1$s võta oma võtmehoidla kasutusele" "Versioon %1$s" + "Kontrollin kasutajakontot" "Logi sisse käsitsi" "Logi sisse serverisse %1$s" "Logi sisse QR-koodi alusel" diff --git a/features/login/impl/src/main/res/values-ro/translations.xml b/features/login/impl/src/main/res/values-ro/translations.xml index ec9b260208..b44242abf8 100644 --- a/features/login/impl/src/main/res/values-ro/translations.xml +++ b/features/login/impl/src/main/res/values-ro/translations.xml @@ -28,7 +28,7 @@ "Care este adresa serverului dumneavoastră?" "Selectați serverul dumneavoastra" "Creați un cont" - "Acest cont a fost dezactivat." + "Acest cont a fost șters." "Utilizator și/sau parolă incorecte" "Acesta nu este un identificator de utilizator valid. Format așteptat: „@user:homeserver.org”" "Acest server este configurat pentru a utiliza token-uri de reîmprospătare. Acestea nu sunt acceptate atunci când utilizați autentificare bazată pe parolă." @@ -37,11 +37,20 @@ "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată." "Bine ați revenit!" "Conectați-vă la %1$s" + "Deschideți Element Clasic" + "Deschideți Element Classic pe dispozitivul dumneavoastră" + "Accesați Setări > Securitate și confidențialitate" + "În Gestionarea cheilor criptografice, selectați Recuperarea mesajelor criptate" + "Urmați instrucțiunile pentru a activa stocarea cheilor" + "Reveniți la %1$s" + "Activați stocarea cheilor înainte de a continua către %1$s" "Versiunea %1$s" + "Se verifică contul…" "Conectați-vă manual" "Conectați-vă la %1$s" "Conectați-vă cu un cod QR" "Creați un cont" + "Bine ați revenit" "Bine ați venit la cel mai rapid %1$s din toate timpurile. Supraalimentat pentru viteză și simplitate." "Bun venit în %1$s. Supraalimentat, pentru viteză și simplitate." "Fii în Elementul tău" @@ -60,6 +69,8 @@ "Cererea de autentificare a fost anulată" "Autentificarea a fost refuzată pe celălalt dispozitiv." "Autentificarea a fost refuzată" + "Nu trebuie să faceți nimic altceva." + "Celălalt dispozitiv este deja conectat" "Autentificarea a expirat. Vă rugăm să încercați din nou." "Autentificarea nu a fost finalizată la timp" "Celălalt dispozitiv nu acceptă autentificarea la %s cu un cod QR. diff --git a/features/logout/impl/src/main/res/values-et/translations.xml b/features/logout/impl/src/main/res/values-et/translations.xml index ee5a2ce9d0..cd29e13354 100644 --- a/features/logout/impl/src/main/res/values-et/translations.xml +++ b/features/logout/impl/src/main/res/values-et/translations.xml @@ -1,18 +1,18 @@ - "Kas sa oled kindel, et soovid välja logida?" + "Kas sa oled kindel, et soovid selle seadme eemaldada?" "Eemalda see seade" "Eemalda see seade" - "Logime välja…" - "Oled oma viimasest seansist välja logimas. Kui logid nüüd välja, kaotad ligipääsu oma krüptitud sõnumitele." - "Sa oled varukoopiate tegemise välja lülitanud" - "Kui su võrguühendus katkes, siis sinu krüptovõtmed oli parasjagu varundamisel. Loo võrguühendus uuesti, oota kuni krüptovõtmete varundamine lõppeb ja alles siis logi rakendusest välja." + "Eemaldan seadet…" + "See on sinu ainus seade. Kui sa selle eemaldad, vajad taastamisvõtit, et kinnitada oma digitaalset identiteeti ja taastada järgmisel sisselogimisel oma krüptitud vestlused." + "Sa kaotad peagi juurdepääsu oma krüptitud vestlustele" + "Kui su võrguühendus katkes, siis sinu krüptovõtmed oli parasjagu varundamisel. Loo võrguühendus uuesti, oota kuni krüptovõtmete varundamine lõppeb ja alles siis eemalda see seade." "Sinu krüptovõtmed on veel varundamisel" - "Enne väljalogimist palun oota, et pooleliolev toiming lõppeb." + "Enne selle seadme eemaldamist palun oota, et pooleliolev toiming lõppeb." "Sinu krüptovõtmed on veel varundamisel" "Eemalda see seade" - "Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis kaotad ligipääsu oma krüptitud sõnumitele." + "See on sinu ainus seade. Kui sa selle eemaldad, vajad taastamisvõtit, et kinnitada oma digitaalset identiteeti ja taastada järgmisel sisselogimisel oma krüptitud vestlused." "Andmete taastamine on seadistamata" "Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis ilmselt kaotad ligipääsu oma krüptitud sõnumitele." - "Kas sa oled oma taastevõtme salvestanud?" + "Enne selle seadme eemaldamist veendu, et sul on juurdepääs taastevõtmele" diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 93ea0d8178..9bf1c5c079 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -1,7 +1,7 @@ "Odesílatel události se neshoduje s vlastníkem zařízení, které ji odeslalo." - "Autenticitu této zašifrované zprávy nelze na tomto zařízení zaručit." + "Pravost této šifrované zprávy nelze na tomto zařízení zaručit." "Zašifrováno dříve ověřeným uživatelem." "Není zašifrováno." "Šifrováno neznámým nebo smazaným zařízením." diff --git a/features/messages/impl/src/main/res/values-et/translations.xml b/features/messages/impl/src/main/res/values-et/translations.xml index 8da4d23f79..506b5ce195 100644 --- a/features/messages/impl/src/main/res/values-et/translations.xml +++ b/features/messages/impl/src/main/res/values-et/translations.xml @@ -35,7 +35,7 @@ "Salvesta video" "Manus" "Fotode ja videote galerii" - "Asukoht" + "Jaga asukohta" "Küsitlus" "Tekstivorming" "Sõnumite ajalugu pole hetkel saadaval" diff --git a/features/messages/impl/src/main/res/values-pl/translations.xml b/features/messages/impl/src/main/res/values-pl/translations.xml index 0085e1d4ee..efb84cd153 100644 --- a/features/messages/impl/src/main/res/values-pl/translations.xml +++ b/features/messages/impl/src/main/res/values-pl/translations.xml @@ -32,7 +32,7 @@ "Powód zgłoszenia treści" "Kamera" "Zrób zdjęcie" - "Nagraj film" + "Nagraj wideo" "Załącznik" "Zdjęcia i filmy" "Udostępnij lokalizację" 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 e51d05c455..5c690dd824 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -87,6 +87,7 @@ Pokud budete pokračovat, některá nastavení se mohou změnit." "systémová nastavení" "Systémová oznámení byla vypnuta" "Oznámení" + "Černý" "Tmavé" "Světlý" "Systém" 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 ba1ee2ac7c..d1e2b91d58 100644 --- a/features/preferences/impl/src/main/res/values-et/translations.xml +++ b/features/preferences/impl/src/main/res/values-et/translations.xml @@ -11,7 +11,10 @@ "Peida jututubade kutsetest tunnuspildid" "Peida meedia eelvaated ajajoonel" "Katsed" + "Vahemaa, mille pead andmete uuenduse käivitamiseks läbima." + "Veendu, et sellel rakendusel on õigus kasutada funktsionaalsust „Täpne asukoht“. Õiguste muutmiseks ava %1$s." "Rakenduse seadistused" + "Andmete uuendused reaalajas asukoha jagamisel" "Iga %1$d meeter" "Iga %1$d meetrit" @@ -81,6 +84,7 @@ Kui sa jätkad muutmist, siis võivad muutuda ka need peidetud eelistused.""süsteemi seadistusi" "Süsteemi teavitused on välja lülitatud" "Teavitused" + "Süsimust kujundus" "Tume" "Hele" "Süsteem" 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 4c3ff5c04d..4c1b988696 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -84,6 +84,7 @@ Ha folytatja, egyes beállítások megváltozhatnak." "rendszerbeállításokat" "A rendszerértesítések ki vannak kapcsolva" "Értesítések" + "Fekete" "Sötét" "Világos" "Rendszer" 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 3fc2a3eac8..83b02c6fe2 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -85,6 +85,7 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "ustawienia systemowe" "Powiadomienia systemowe wyłączone" "Powiadomienia" + "Czarny" "Ciemny" "Jasny" "System" diff --git a/features/preferences/impl/src/main/res/values-ro/translations.xml b/features/preferences/impl/src/main/res/values-ro/translations.xml index 18f8326bf3..69eddf05f5 100644 --- a/features/preferences/impl/src/main/res/values-ro/translations.xml +++ b/features/preferences/impl/src/main/res/values-ro/translations.xml @@ -11,6 +11,15 @@ "Ascundeți avatarele din invitațiile pentru camere" "Ascundeți previzualizările media în lista de mesaje" "Laboratoare" + "Distanța pe care trebuie să o parcurgeți pentru a declanșa o actualizare." + "Asigurați-vă că este activată opțiunea „Locație precisă” pentru această aplicație. Pentru a schimba permisiunea, accesați %1$s." + "Setările aplicației" + "Actualizări in timp real ale locației" + + "La fiecare %1$d metru" + "La fiecare %1$d metri" + "La fiecare %1$d metri" + "Încărcați fotografii și videoclipuri mai rapid și reduceți consumul de date" "Optimizați calitatea media" "Moderare și siguranță" @@ -78,6 +87,7 @@ Dacă continuați, unele dintre setările dumneavoastră pot fi modificate.""Setări de sistem" "Notificările de sistem sunt dezactivate" "Notificări" + "Negru" "Întunecat" "Deschis" "Sistem" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index e91bc58752..8f6de8209e 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -78,11 +78,12 @@ "全部" "提及" "通知我以下类型" - "我在房间中被提及时通知我" + "提及所有成员(@room)时通知我" "要接收通知,请更改 %1$s。" "系统设置" "系统通知已关闭" "通知" + "纯黑" "深色" "浅色" "系统" diff --git a/features/reportroom/impl/src/main/res/values-zh/translations.xml b/features/reportroom/impl/src/main/res/values-zh/translations.xml index e79148a45d..4366e33455 100644 --- a/features/reportroom/impl/src/main/res/values-zh/translations.xml +++ b/features/reportroom/impl/src/main/res/values-zh/translations.xml @@ -2,7 +2,7 @@ "你的举报已成功提交,但在尝试退出房间时遇到问题。请重试。" "无法离开房间" - "向管理员举报此房间。如果信息已加密,管理员将无法读取。" + "向管理员举报此房间。如果消息已加密,管理员将无法读取。" "描述举报的理由…" "举报房间" diff --git a/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml index 87936de3a7..662096dd20 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-cs/translations.xml @@ -6,6 +6,7 @@ "Odstranit zprávy" "Člen" "Pozvat přátele" + "Sdílet aktuální polohu" "Správa prostoru" "Spravovat místnosti" "Spravovat členy" diff --git a/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml index 1c1f490580..02ec4ad07d 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-et/translations.xml @@ -6,6 +6,7 @@ "Eemalda sõnumid" "Liikmed" "Osalejate kutsumine" + "Jaga asukohta reaalajas" "Halda kogukonda" "Halda jututuba" "Liikmete haldus" diff --git a/features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml index ac9ec710a2..962f8b2d4d 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-ro/translations.xml @@ -6,6 +6,7 @@ "Ștergeți mesajele" "Membru" "Invitați persoane" + "Partajați locația în timp real" "Gestionați spațiul" "Gestionați camerele" "Gestionați membrii" diff --git a/features/roomdetails/impl/src/main/res/values-pl/translations.xml b/features/roomdetails/impl/src/main/res/values-pl/translations.xml index 7af5d8e557..ccc359ed25 100644 --- a/features/roomdetails/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pl/translations.xml @@ -108,8 +108,8 @@ "Zezwalaj na ustawienia niestandardowe" "Włączenie tej opcji nadpisze ustawienie domyślne" "Powiadamiaj mnie o tym czacie przez" - "Możesz to zmienić w swoim %1$s." - "ustawienia globalne" + "Możesz to zmienić w %1$s." + "ustawieniach globalnych" "Ustawienie domyślne" "Usuń ustawienia własne" "Wystąpił błąd podczas ładowania ustawień powiadomień." diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index 0374c64314..425e35a23c 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -1,5 +1,8 @@ + "Membrii noi nu pot vedea istoricul" + "Membrii noi pot vedea istoricul" + "Oricine poate vedea istoricul" "Veți avea nevoie de o adresă pentru a o face vizibilă în directorul public." "Editați adresa" "A apărut o eroare în timpul actualizării setărilor pentru notificari." @@ -135,6 +138,7 @@ "Adăugați o adresă" "Oricine se află în spațiile autorizate se poate alătura, dar toți ceilalți trebuie să solicite accesul." "Toată lumea trebuie să solicite acces." + "Solicitați să vă alăturați" "Oricine în %1$s se poate alătura, dar toți ceilalți trebuie să solicite acces." "Da, activați criptarea" "Odată activată, criptarea pentru o cameră nu poate fi dezactivată. Mesajele anterioare vor fi vizibile numai pentru membrii camerei de la momentul la care au fost invitați sau de la momentul la care s-au alăturat camerei. @@ -145,22 +149,26 @@ Nu recomandăm activarea criptării pentru camerele pe care oricine le poate gă "Criptare" "Activați criptarea end-to-end" "Oricine se poate alătura." - "Alegeți membrii căror spații se pot alătura acestei camere fără invitație. %1$s" + "Oricine" + "Alegeți membrii căror spații se pot alătura acestei cameră fără invitație. %1$s" "Gestionați spațiile" "Doar persoanele invitate se pot alătura." "Doar pe bază de invitație" "Acces" "Oricine se află într-un spațiu autorizat poate participa." "Oricine din %1$s se poate alătura." + "Membrii spațiului" "Spațiile nu sunt momentan suportate." "Veți avea nevoie de o adresă pentru a o face vizibilă în directorul public." "Adresă" "Permiteți găsirea acestei camere prin căutarea în directorul de camere publice al %1$s" "Permiteți găsirea prin căutarea în directorul public." "Vizibilă în directorul de camere publice" + "Oricine (istoricul este public)" + "Modificările nu vor afecta mesajele anterioare, ci doar pe cele noi. %1$s" "Cine poate citi mesajele anterioare" - "Doar pentru membri, de la momentul în care au fost invitați" - "Doar pentru membri, după selectarea acestei opțiuni" + "Membri de la momentul invitației" + "Membri (istoric complet)" "Adresele camerelor sunt modalități de a găsi și accesa camere. Acest lucru vă asigură, de asemenea, că puteți partaja cu ușurință camera dumneavoastră cu alte persoane. Puteți alege să publicați camera în directorul public al camerelor serverului dumneavoastră." "Publicare cameră" 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 c3d849f37b..860d017781 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -2,16 +2,17 @@ "Lülita võtmete varundamine välja" "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." + "Salvesta oma digitaalne identiteet 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." + "Sinu vestluste varundamiseks 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." + "Kui sa oled kaotanud ligipääsu kõikidele oma olemasolevatele seadmetele, siis sa saad taastevõtme abil taastada ligipääsu oma digitaalsele identiteedile ja sõnumite ajaloole." "Sisesta taastevõti" "Sinu krüptovõtmete varundus pole hetkel enam sünkroonis." - "Seadista andmete taastamine" + "Seadista taastevõti" + "Sinu vestlused on automaatselt varundatud kasutades läbivat krüptimist. Kui peaksid kaotama ligipääsu kõikidele oma seadmetele, siis selle varukoopia taastamiseks ja oma digitaalse identiteedi säilitamiseks, on vaja taastevõtit." "Ava %1$s töölauaga seadmes" "Logi uuesti sisse oma kasutajakontole" "Kui sul palutakse seadet verifitseerida, vali %1$s" @@ -23,12 +24,12 @@ "Sinu kasutajakonto andmed, kontaktid, eelistused ja vestluste loend säiluvad" "Sa kaotad seniste sõnumite ajaloo" "Sa pead kõik oma olemasolevad seadmed ja kontaktid uuesti verifitseerima" - "Lähtesta oma identiteet vaid siis, kui sul pole ligipääsu mitte ühelegi oma seadmele ja sa oled kaotanud oma taastevõtme." - "Kui sa ühtegi muud võimalust ei leia, siis lähtesta oma identiteet." - "Lülita välja" - "Kui sa logid välja kõikidest oma seadmetest, siis sa kaotad ligipääsu oma krüptitud sõnumitele." - "Kas sa oled kindel, et soovid varukoopiate tegemise välja lülitada?" - "Varunduse väljalülitamisel kustutatakse hetkel olemasolev sinu krüptovõtmete varukoopia ning lülituvad välja veel mõned turvafunktsionaalsused. Sellisel juhul sul:" + "Lähtesta oma digitaalne identiteet vaid siis, kui sul pole ligipääsu mitte ühelegi oma seadmele ja sa oled kaotanud oma taastevõtme." + "Kui sa ühtegi muud võimalust ei leia, siis lähtesta oma digitaalne identiteet." + "Kustuta" + "Kui sa eemaldad kõik oma seadmed, siis sa kaotad ligipääsu oma krüptitud sõnumitele ja pead oma digitaalse identiteedi lähtestama." + "Kas oled kindel, et soovid võtmehoidla kustutada?" + "Võtmehoidla kustutamine eemaldab sinu digitaalse identiteedi ja sõnumivõtmed serverist ning lülitab välja järgmised turvafunktsionaalsused:" "sul ei ole krüptitud sõnumite ajalugu uutes seadmetes" "sa kaotad ligipääsu oma krüptitud sõnumitele, kui sa logid kõikjal välja rakendusest %1$s" "Kas sa oled kindel, et soovid varunduse välja lülitada?" @@ -58,12 +59,12 @@ "Loo oma taastevõti" "Ära jaga seda kellegagi" "Andmete taastamise seadistamine õnnestus" - "Seadista andmete taastamine" + "Seadista taastevõti" "Jah, lähtesta nüüd" "See tegevus on tagasipöördumatu." - "Kas sa oled kindel, et soovid oma võrguidentiteeti lähtestada?" + "Kas sa oled kindel, et soovid oma digitaalse identiteedi lähtestada?" "Tekkis teadmata viga. Palun kontrolli, kas sinu kasutajakonto salasõna on õige ja proovi uuesti." "Sisesta…" - "Palun kinnita, et soovid oma võrguidentiteedi lähtestada." + "Palun kinnita, et soovid oma digitaalse identiteedi lähtestada." "Jätkamaks sisesta oma kasutajakonto salasõna" 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 f2f44e10f3..99bbcc8159 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -30,7 +30,7 @@ "Jeśli usuniesz wszystkie swoje urządzenia, stracisz zaszyfrowaną historię wiadomości i będziesz musiał zresetować swoją tożsamość cyfrową." "Czy na pewno chcesz usunąć magazyn kluczy?" "Usunięcie magazynu kluczy usunie Twoją tożsamość cyfrową i klucze wiadomości z serwera, wyłączając następujące funkcje bezpieczeństwa:" - "Posiadał historii wiadomości szyfrowanych na nowych urządzeniach" + "Stracisz dostęp do zaszyfrowanej historii wiadomości na nowych urządzeniach" "Utracisz dostęp do wiadomości szyfrowanych, jeśli zostaniesz wszędzie wylogowany z %1$s" "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ł." 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 452a0543c8..2351a54923 100644 --- a/features/securebackup/impl/src/main/res/values-ro/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ro/translations.xml @@ -12,6 +12,7 @@ "Introduceți cheia de recuperare" "Backup-ul pentru chat nu este sincronizat în prezent." "Obțineți cheia de recuperare" + "Chaturile dumneavoastră sunt salvate automat cu criptare end-to-end. Pentru a restaura această copie de rezervă și a vă păstra identitatea digitală atunci când pierdeți accesul la toate dispozitivele dumneavoastră, veți avea nevoie de cheia de recuperare." "Deschideți %1$s pe un dispozitiv desktop" "Conectați-vă din nou la contul dumneavoastră" "Când vi se cere să vă verificați dispozitivul, selectați%1$s" diff --git a/features/securityandprivacy/impl/src/main/res/values-ro/translations.xml b/features/securityandprivacy/impl/src/main/res/values-ro/translations.xml index 83292637ce..2f3eaab319 100644 --- a/features/securityandprivacy/impl/src/main/res/values-ro/translations.xml +++ b/features/securityandprivacy/impl/src/main/res/values-ro/translations.xml @@ -10,6 +10,7 @@ "Adăugați o adresă" "Oricine se află în spațiile autorizate se poate alătura, dar toți ceilalți trebuie să solicite accesul." "Toată lumea trebuie să solicite acces." + "Solicitați să vă alăturați" "Oricine în %1$s se poate alătura, dar toți ceilalți trebuie să solicite acces." "Da, activați criptarea" "Odată activată, criptarea pentru o cameră nu poate fi dezactivată. Mesajele anterioare vor fi vizibile numai pentru membrii camerei de la momentul la care au fost invitați sau de la momentul la care s-au alăturat camerei. @@ -20,22 +21,26 @@ Nu recomandăm activarea criptării pentru camerele pe care oricine le poate gă "Criptare" "Activați criptarea end-to-end" "Oricine se poate alătura." - "Alegeți membrii căror spații se pot alătura acestei camere fără invitație. %1$s" + "Oricine" + "Alegeți membrii căror spații se pot alătura acestei cameră fără invitație. %1$s" "Gestionați spațiile" "Doar persoanele invitate se pot alătura." "Doar pe bază de invitație" "Acces" "Oricine se află într-un spațiu autorizat poate participa." "Oricine din %1$s se poate alătura." + "Membrii spațiului" "Spațiile nu sunt momentan suportate." "Veți avea nevoie de o adresă pentru a o face vizibilă în directorul public." "Adresă" "Permiteți găsirea acestei camere prin căutarea în directorul de camere publice al %1$s" "Permiteți găsirea prin căutarea în directorul public." "Vizibilă în directorul de camere publice" + "Oricine (istoricul este public)" + "Modificările nu vor afecta mesajele anterioare, ci doar pe cele noi. %1$s" "Cine poate citi mesajele anterioare" - "Doar pentru membri, de la momentul în care au fost invitați" - "Doar pentru membri, după selectarea acestei opțiuni" + "Membri de la momentul invitației" + "Membri (istoric complet)" "Adresele camerelor sunt modalități de a găsi și accesa camere. Acest lucru vă asigură, de asemenea, că puteți partaja cu ușurință camera dumneavoastră cu alte persoane. Puteți alege să publicați camera în directorul public al camerelor serverului dumneavoastră." "Publicare cameră" diff --git a/features/space/impl/src/main/res/values-ro/translations.xml b/features/space/impl/src/main/res/values-ro/translations.xml index 2bf5d0e251..7748f44ee2 100644 --- a/features/space/impl/src/main/res/values-ro/translations.xml +++ b/features/space/impl/src/main/res/values-ro/translations.xml @@ -9,10 +9,21 @@ "Selectați camerele pe care doriți să le părăsiți și în care nu sunteți singurul administrator:" "Trebuie să desemnați un alt administrator pentru acest spațiu înainte de a-l părăsi." + "Sunteți singurul proprietar al %1$s. Trebuie să transferați dreptul de proprietate către altcineva înainte de a parăsi camera." "Nu veți părăsi următoarele camere deoarece sunteți singurul administrator:" "Părăsiți %1$s?" "Sunteți singurul administrator pentru %1$s" + "Transferați proprietatea" + "Cameră" + "Adăugarea unei camere nu va afecta accesul la cameră. Pentru a modifica accesul, accesați Setări cameră > Securitate și confidențialitate." + "Adăugați prima dumneavoastră cameră" "Vizualizați membrii" + "Eliminarea unei camere nu va afecta accesul la aceasta. Pentru a modifica accesul, accesați Informații despre cameră > Confidențialitate și securitate." + + "Eliminați camera %1$d din %2$s" + "Eliminați camerele %1$d din %2$s" + "Eliminați camerele %1$d din %2$s" + "Părăsiți spațiul" "Roluri și permisiuni" "Securitate & confidențialitate" diff --git a/features/verifysession/impl/src/main/res/values-et/translations.xml b/features/verifysession/impl/src/main/res/values-et/translations.xml index f09202c9c2..25cf6272fe 100644 --- a/features/verifysession/impl/src/main/res/values-et/translations.xml +++ b/features/verifysession/impl/src/main/res/values-et/translations.xml @@ -2,7 +2,7 @@ "Kas kinnitamine pole võimalik?" "Loo uus taastevõti" - "Krüptitud sõnumivahetuse tagamiseks verifitseeri see seade." + "Turvalise sõnumside seadistamiseks vali verifitseerimise viis." "Kinnita oma digitaalne identiteet" "Kasuta teist seadet" "Kasuta taastevõtit" @@ -17,7 +17,7 @@ "Kinnita, et kõik järgnevalt kuvatud numbrid on täpselt samad, mida sa näed oma teises sessioonis." "Võrdle numbreid" "Võid nüüd sõnumeid oma teises seadmes turvaliselt saata ja vastu võtta." - "Nüüd sa võid sõnumite vastuvõtmisel ja saatmisel selle kasutaja identiteeti usaldada." + "Nüüd sa võid sõnumite vastuvõtmisel ja saatmisel selle kasutaja digitaalset identiteeti usaldada." "Seade on verifitseeritud" "Sisesta taastevõti" "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus." @@ -42,7 +42,7 @@ "Ava rakendus teises verifitseeritud seadmes" "Lisaturvalisuse nimel verifitseeri seee kasutaja, võrreldes oma seadmetes olevaid emojisid. Tee seda, kasutades usaldusväärset suhtlusviisi." "Kas verifitseerime selle kasutaja?" - "Lisaturvalisuse nimel soovib teine kasutaja sinu identiteeti verifitseerida. Järgmiseks näed sa emojisid, mida peate omavahel võrdlema." + "Lisaturvalisuse nimel soovib teine kasutaja sinu digitaalse identiteeti verifitseerida. Järgmiseks näed sa emojisid, mida peate omavahel võrdlema." "Sa peaksid teises seadmes nägema hüpikakent. Palun alusta sealt verifitseerimist." "Alusta verifitseerimist teises seadmes" "Alusta verifitseerimist teises seadmes" @@ -50,5 +50,5 @@ "Kui oled nõustunud, siis saad sa verifitseerimist jätkata." "Jätkamaks nõustu verifitseerimisprotsessi alustamisega oma teises sessioonis." "Ootame nõustumist verifitseerimispäringuga" - "Logime välja…" + "Eemaldan seadet…" diff --git a/features/verifysession/impl/src/main/res/values-ro/translations.xml b/features/verifysession/impl/src/main/res/values-ro/translations.xml index cb22fd02d9..25f897d954 100644 --- a/features/verifysession/impl/src/main/res/values-ro/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ro/translations.xml @@ -17,7 +17,7 @@ "Confirmați că numerele de mai jos se potrivesc cu cele afișate în cealaltă sesiune." "Comparați numerele" "Noua dumneavoastră sesiune este acum verificată. Are acces la mesajele dumneavoastră criptate, iar ceilalti utilizatori vă vor vedea ca fiind de încredere." - "Acum puteți avea încredere în identitatea acestui utilizator atunci când trimiteți sau primiți mesaje." + "Acum puteți avea încredere în identitatea digitală a acestui utilizator atunci când trimiteți sau primiți mesaje." "Dispozitiv verificat" "Introduceți cheia de recuperare" "Fie cererea a expirat, cererea a fost respinsă, fie a existat o nepotrivire de verificare." @@ -42,7 +42,7 @@ "Deschideți aplicația pe un alt dispozitiv verificat" "Pentru securitate suplimentară, verificați acest utilizator comparând un set de emoji-uri pe dispozitivele dvs. Faceți acest lucru utilizând o metodă de comunicare de încredere." "Verificați acest utilizator?" - "Pentru o securitate suplimentară, un alt utilizator dorește să vă verifice identitatea. Vi se va afișa un set de emoji-uri pentru comparație." + "Pentru o securitate sporită, un alt utilizator dorește să vă verifice identitatea digitală. Vi se va afișa un set de emoji-uri pentru comparație." "Ar trebui să vedeți o fereastră pop-up pe celălalt dispozitiv. Începeți verificarea de acolo acum." "Începeți verificarea pe celălalt dispozitiv" "Începeți verificarea pe celălalt dispozitiv" diff --git a/libraries/eventformatter/impl/src/main/res/values-pl/translations.xml b/libraries/eventformatter/impl/src/main/res/values-pl/translations.xml index e45227ad62..94e1015375 100644 --- a/libraries/eventformatter/impl/src/main/res/values-pl/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-pl/translations.xml @@ -29,7 +29,7 @@ "Zaprosiłeś %1$s" "%1$s zaprosił Cię" "%1$s dołączył do pokoju" - "Dołączyłeś(aś) do pokoju" + "Dołączyłeś do pokoju" "%1$s prosi o możliwość dołączenia" "%1$s zezwolił %2$s na dołączenie" "Zezwoliłeś %1$s na dołączenie" diff --git a/libraries/matrixui/src/main/res/values-et/translations.xml b/libraries/matrixui/src/main/res/values-et/translations.xml index abd3830768..a84990af10 100644 --- a/libraries/matrixui/src/main/res/values-et/translations.xml +++ b/libraries/matrixui/src/main/res/values-et/translations.xml @@ -3,6 +3,7 @@ "Saada kutse" "Kas sa soovid alustada vestlust kasutajaga %1$s?" "Kas saadame kutse?" + "Sul pole hetkel selle inimesega ühtegi vestlust. Enne jätkamist kinnita talle kutse saatmine." "Kas alustad vestlust selle uue kontaktiga?" "%1$s (%2$s) saatis sulle kutse" diff --git a/libraries/matrixui/src/main/res/values-ro/translations.xml b/libraries/matrixui/src/main/res/values-ro/translations.xml index 5156d6bd16..4d3eaa2364 100644 --- a/libraries/matrixui/src/main/res/values-ro/translations.xml +++ b/libraries/matrixui/src/main/res/values-ro/translations.xml @@ -3,5 +3,7 @@ "Trimiteți invitația" "Doriți să începeți o discuție cu %1$s?" "Trimiteți invitația?" + "În prezent, nu aveți nicio chat cu această persoană. Confirmați invitația înainte de a continua." + "Începeți o conversație cu acest nou contact?" "%1$s (%2$s) v-a invitat." diff --git a/libraries/mediaviewer/impl/src/main/res/values-cs/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-cs/translations.xml index bcb7b64237..654aa869a6 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-cs/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-cs/translations.xml @@ -16,6 +16,7 @@ "Název souboru" "Žádné další soubory k zobrazení" "Žádná další média k zobrazení" + "Informace o souboru" "Nahrál(a)" "Nahráno" diff --git a/libraries/mediaviewer/impl/src/main/res/values-et/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-et/translations.xml index 63329a797d..f982b32433 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-et/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-et/translations.xml @@ -16,6 +16,7 @@ "Failinimi" "Pole enam kuvatavaid faile" "Pole enam kuvatavat meediat" + "Faili teave" "Üleslaadija" "Üleslaaditud" diff --git a/libraries/mediaviewer/impl/src/main/res/values-ro/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ro/translations.xml index bfd1d49c2b..aa713361d0 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-ro/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-ro/translations.xml @@ -16,6 +16,7 @@ "Nume fișier" "Nu mai există fișiere de afișat" "Nu mai există conținut media de afișat" + "Informații despre fișier" "Încărcat de" "Încărcat la" diff --git a/libraries/push/impl/src/main/res/values-et/translations.xml b/libraries/push/impl/src/main/res/values-et/translations.xml index 4b00b7b5e7..49010359cd 100644 --- a/libraries/push/impl/src/main/res/values-et/translations.xml +++ b/libraries/push/impl/src/main/res/values-et/translations.xml @@ -19,7 +19,8 @@ "Sul on %d uus sõnum." "Sul on %d uut sõnumit." - "📹 Sissetulev kõne" + "📞 Saabuv kõne" + "📹 Saabuv kõne" "** Saatmine ei õnnestunud - palun ava jututoa täisvaade" "Liitu" "Keeldu" diff --git a/libraries/push/impl/src/main/res/values-ro/translations.xml b/libraries/push/impl/src/main/res/values-ro/translations.xml index 0a83e6afad..2f677568ea 100644 --- a/libraries/push/impl/src/main/res/values-ro/translations.xml +++ b/libraries/push/impl/src/main/res/values-ro/translations.xml @@ -15,10 +15,16 @@ "Distribuitorul de notificări UnifiedPush nu a putut fi înregistrat, așadar nu veți mai primi notificări. Verificați setările de notificări ale aplicației și starea distribuitorului push." "Aveți mesaje noi" + + "Aveți %d mesaj nou." + "Aveți %d mesaje noi." + "Aveți %d mesaje noi." + + "📞 Apel primit" "Apel primit" "** Trimiterea eșuată - vă rugăm să deschideți camera" "Alăturați-vă" - "Respinge" + "Respingeți" "%d invitație" "%d invitații" 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 36bd188c2a..1bf3802b12 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -15,6 +15,7 @@ "Podrobnosti o šifrování" "Rozbalit textové pole zprávy" "Skrýt heslo" + "Informace" "Připojit se k hovoru" "Přejít dolů" "Přesunout mapu na mou polohu" @@ -50,6 +51,7 @@ "Odeslat soubory" "Poloha odesílatele" "Vyžaduje se časově omezená akce, na ověření máte jednu minutu" + "Nastavení, vyžaduje akci" "Zobrazit heslo" "Zahájit hovor" "Zahájit videohovor" @@ -71,6 +73,7 @@ "Hovor" "Zrušit" "Prozatím zrušit" + "Vyberte soubor" "Vybrat fotku" "Vymazat" "Zavřít" @@ -205,7 +208,9 @@ "Beta" "Blokovaní uživatelé" "Bubliny" + "Hovor odmítnut" "Hovor zahájen" + "Odmítli jste hovor" "Záloha chatu" "Zkopírováno do schránky" "Autorská práva" @@ -466,6 +471,9 @@ Opravdu chcete pokračovat?" "Omlouváme se, došlo k chybě" "🔐️ Připojte se ke mně na %1$s" "Ahoj, ozvi se mi na %1$s: %2$s" + "Sdílení polohy v reálném čase" + "Probíhá sdílení polohy" + "%1$s Aktuální poloha" "%1$s Android" "Zatřeste zařízením pro nahlášení chyby" "Snímek obrazovky" 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 1669abc4e0..eef46172bd 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -14,6 +14,7 @@ "Krüptimise üksikasjad" "Laienda tekstivälja" "Peida salasõna" + "Teave" "Liitu kõnega" "Mine lõppu" "Nihuta kaart minu asukohta" @@ -48,6 +49,7 @@ "Saada faile" "Saatja asukoht" "Palun tee see ajapiiranguga toiming, sul on aega üks minut" + "Seaded, vajalik on tegevus" "Näita salasõna" "Helista" "Alusta videokõnet" @@ -231,6 +233,7 @@ "Tühi fail" "Krüptimine" "Krüptimine on kasutusel" + "Lõpeb kell %1$s" "Sisesta oma PIN-kood" "Viga" "Tekkis viga ja sa ei pruugi enam saada uute sõnumite kohta teavitusi. Palun kontrolli teavituste seadistusi ja proovi viga tuvastada. @@ -354,9 +357,10 @@ Põhjus: %1$s." "Seadistused" "Jaga kogukonda" "Uued liikmed näevad ajalugu" + "Reaalajas jagatud asukoht" "Jagatud asukoht" "Jagatud kogukond" - "Logime välja" + "Seade on eemaldamisel" "Midagi läks valesti" "Tekkis viga. Palun proovi uuesti." "Kogukond" @@ -381,7 +385,7 @@ Põhjus: %1$s." "Dekrüptimine ei olnud võimalik" "Saadetud ebaturvalisest seadmest" "Sul pole ligipääsu antud sõnumile" - "Saatja verifitseeritud identiteet on lähtestatud" + "Saatja digitaalse identiteet on lähtestatud" "Kutset polnud võimalik saata ühele või enamale kasutajale." "Kutse(te) saatmine ei õnnestunud" "Eemalda lukustus" @@ -412,12 +416,13 @@ Põhjus: %1$s." "Kuna sind polnud saatmise ajal jututoas, siis %1$s (%2$s) jagas seda sõnumit sinuga." "%1$s jagas seda sõnumit, kuna sind ei olnud selle algse saatmise ajal jututoas." "See jututuba on seadistatud sedaviisi, et ka uued liikmed saavad lugeda varasemat ajalugu. %1$s" - "Kasutaja %1$s võrguidentiteet on lähtestatud. %2$s" - "Kasutaja %1$s %2$s võrguidentiteet on lähtestatud. %3$s" + "Kasutaja %1$s digitaalse identiteet on lähtestatud. %2$s" + "Kasutaja %1$s %2$s digitaalse identiteet on lähtestatud. %3$s" "(%1$s)" - "%1$s kasutaja verifitseeritud identiteet on lähtestatud." - "%1$s kasutaja (%2$s kasutajanimi) verifitseeritud identiteet on lähtestatud. %3$s" + "%1$s kasutaja digitaalse identiteet on lähtestatud." + "%1$s kasutaja (ksutajanimega %2$s) digitaalse identiteet on lähtestatud. %3$s" "Võta verifitseerimine tagasi" + "Luba juurdepääs" "%1$s link viib sind teise veebisaiti %2$s Kas sa oled kindel, et soovid jätkata?" @@ -447,6 +452,7 @@ Kas sa oled kindel, et soovid jätkata?" "Rakendus %1$s ei suutnud tuvastada sinu asukohta. Palun proovi hiljem uuesti." "Sinu häälsõnumi üleslaadimine ei õnnestunud." "Seda jututuba pole enam olemas või pole see kutse enam kehtiv." + "Asukohapõhise funktsionaalsuse kasutamiseks palun lülita nutiseadmes GPS sisse." "Sõnumit ei leidu" "Rakendusel %1$s puudub õigus sinu asukohta tuvastada. Sa saad seda lubada süsteemi seadistustest." "Rakendusel %1$s puudub õigus sinu asukohta tuvastada. Järgnevalt anna vastavad õigused." @@ -458,6 +464,9 @@ Kas sa oled kindel, et soovid jätkata?" "Vabandust, ilmnes viga" "🔐️ Liitu minuga rakenduses %1$s" "Hei, suhtle minuga %1$s võrgus: %2$s" + "Asukoha jagamine reaalajas" + "Asukoha jagamine käimas" + "Asukoht reaalajas: %1$s" "%1$s Android" "Veast teatamiseks raputa nutiseadet ägedalt" "Ekraanitõmmis" @@ -480,11 +489,11 @@ Kas sa oled kindel, et soovid jätkata?" "%1$d esiletõstetud sõnumit" "Esiletõstetud sõnumid" - "Oma võrguidentiteedi lähtestamiseks suuname sind %1$s kasutajakonto halduse lehele. Hiljem suunatakse sind tagasi sama rakenduse juurde." - "Sa ei saa seda kinnitada? Ava oma kasutajakonto haldus ja lähtesta oma võrguidentiteet." + "Oma digitaalse identiteedi lähtestamiseks suuname sind %1$s kasutajakonto halduse lehele. Hiljem suunatakse sind tagasi sama rakenduse juurde." + "Sa ei saa seda kinnitada? Ava oma kasutajakonto haldus ja lähtesta oma digitaalne identiteet." "Unusta verifitseerimine ja saada ikkagi" "Sa võid jätta verifitseerimisvea tähelepanuta ja sõnumi ikkagi saata või katkestad saatmise ja peale kasutaja %1$s verifitseerimist proovid seda uuesti." - "Sinu sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on lähtestatud." + "Sinu sõnum on saatmata, kuna kasutaja %1$s digitaalse identiteet on lähtestatud." "Saada sõnum ikkagi" "%1$s kasutab ühte või enamat verifitseerimata seadet. Sa võid sõnumi ikkagi saata või katkestad selle ning ootad kuni %2$s on kõik oma seadmed verifitseerinud ning proovid seejärel uuesti." "Sinu sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid" @@ -508,14 +517,16 @@ Kas sa oled kindel, et soovid jätkata?" "Ava Apple Mapsis" "Ava Google Mapsis" "Ava OpenStreetMapis" - "Jaga seda asukohta" + "Jaga valitud asukohta" "Jagamise valikud" "Sinu loodud kogukonnad ning need, millega oled liitunud." "%1$s • %2$s" "Jututubade haldamiseks võid luua kogukondi" "Kogukond: %1$s" "Kogukonnad" - "Sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on lähtestatud." + "Jagatud %1$s" + "Kaardil" + "Sõnum on saatmata, kuna kasutaja %1$s digitaalse identiteet on lähtestatud." "Sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid." "Kuna sa pole üks või enamgi oma seadet verifitseerinud, siis sinu sõnum on saatmata." "Asukoht" @@ -525,5 +536,5 @@ Kas sa oled kindel, et soovid jätkata?" "Ligipääsuks vanadele sõnumitele pead selle seadme verifitseerima" "Sul pole ligipääsu antud sõnumile" "Sõnumi dekrüptimine ei õnnestu" - "Kuna seade on verifitseerimata või saatja pole sind verifitseerinud, siis sõnumi näitamine on blokeeritud." + "Kuna seade on verifitseerimata või saatja pole sinu digitaalset identiteeti verifitseerinud, siis sõnumi näitamine on blokeeritud." diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index d5093590ca..b3f0f32639 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -235,7 +235,7 @@ Syy: %1$s." "Epäonnistui" "Lisää suosikkeihin" "Lisätty suosikkeihin" - "Synkronoidaan ilmoituksia…" + "Synkronoidaan ilmoituksia…" "Tiedosto" "Tiedosto poistettu" "Tiedosto tallennettu" 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 fb6b06daf7..c654a66541 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -409,7 +409,7 @@ Powód: %1$s." "Zweryfikuj urządzenie" "Zweryfikuj tożsamość" "Zweryfikuj użytkownika" - "Film" + "Wideo" "Wysoka jakość" "Najlepsza jakość, większy rozmiar pliku" "Niska jakość" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 35930b694e..dfd68686ce 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -15,6 +15,7 @@ "Detalii privind criptarea" "Extindeți câmpul mesajului" "Ascundeți parola" + "Informații" "Alăturați-vă apelului" "Mergeți în jos" "Mutați harta la locația mea" @@ -28,9 +29,12 @@ "Pauză" "Mesaj vocal, durată:%1$s, poziție curentă: %2$s" "Câmp PIN" + "Locație fixată" "Redați" + "Viteză de redare" "Sondaj" "Sondaj încheiat" + "Cod QR" "Reacționați cu %1$s" "Reacționați cu alte emoji-uri" "Citit de %1$s și %2$s" @@ -45,9 +49,13 @@ "Îndepărtați reacția %1$s" "Avatarul camerei" "Trimiteți fișiere" + "Locația expeditorului" "Acțiune limitată în timp necesară, aveți un minut pentru a verifica" + "Setări, acțiune necesară" "Afișați parola" "Începeți un apel" + "Inițiați un apel video" + "Inițiați un apel vocal" "Cameră terminată" "Avatar utilizator" "Meniu utilizator" @@ -59,15 +67,17 @@ "Avatarul dumneavoastră" "Acceptați" "Adăugați o descriere" + "Adăugați camere existente" "Adăugați listei de mesaje" "Înapoi" - "Apel" + "Apelați" "Anulați" "Anulați pentru moment" + "Alegeți fișierul" "Alegeți o fotografie" "Ștergeți" "Închideți" - "Verificare completă" + "Finalizați verificarea" "Confirmați" "Confirmați parola" "Continuați" @@ -78,22 +88,27 @@ "Copiați textul" "Creați" "Creați o cameră" + "Creeați spațiu" "Dezactivați" "Dezactivați contul" "Refuzați" "Refuzați și blocați" + "Ștergere" + "Ștergeți contul" "Ștergeți sondajul" "Deselectați tot" "Dezactivați" "Renunţare" "Renunțați" - "Efectuat" + "Terminat" + "Descărcare" "Editați" "Editați descrierea" "Editați sondajul" "Activați" "Închideți sondajul" "Introduceți PIN-ul" + "Explorați spațiile publice" "Finalizați" "Ați uitat parola?" "Redirecționați" @@ -113,10 +128,11 @@ "Părăsiți camera" "Părăsiți spațiul" "Încărcați mai mult" - "Administrare cont" - "Gestionare dispozitive" + "Gestionați contul" + "Gestionați contul și dispozitivele" + "Gestionați dispozitivele" "Gestionați camerele" - "Mesaj" + "Contactați" "Minimizați" "Următorul" "Nu" @@ -125,11 +141,11 @@ "Deschideți meniul contextual" "Setări" "Deschideți cu" - "Fixează" + "Fixați" "Raspuns rapid" - "Citat" + "Citați" "Reacționați" - "Respinge" + "Respingeți" "Indepărtați" "Ștergeți descrierea" "Ștergeți mesajul" @@ -140,7 +156,7 @@ "Raportați conținutul" "Raportați conversația" "Raportați camera" - "Resetare" + "Resetați" "Resetați identitatea" "Reîncercați" "Reîncercați decriptarea" @@ -153,7 +169,8 @@ "Trimiteți un mesaj vocal" "Partajați" "Partajați linkul" - "Afișare" + "Partajați locația în timp real" + "Afișați" "Autentificați-vă din nou" "Deconectați-vă" "Deconectați-vă oricum" @@ -163,10 +180,12 @@ "Începeți din nou" "Începeți verificarea" "Atingeți pentru a încărca harta" + "Opriți" "Faceți o fotografie" "Atingeți pentru opțiuni" + "Traduceți" "Încercați din nou" - "Defixeaza" + "Defixați" "Vizualizați" "Vedeți în lista de mesaje" "Vedeți sursă" @@ -176,8 +195,8 @@ "Upgrade disponibil" "Despre" "Politică de utilizare rezonabilă" - "Adăugați un cont" - "Adăugați un alt cont" + "Adăugare cont" + "Adăugare cont" "Adăugare descriere" "Setări avansate" "o imagine" @@ -189,14 +208,17 @@ "Beta" "Utilizatori blocați" "Baloane" - "A început un apel" + "Apel respins" + "Apel inițiat" + "Ați respins un apel" "Backup conversații" "Copiat în clipboard" "Drepturi de autor" "Se creează camera…" + "Se crează spațiul" "Cerere anulată" - "Ați parăsit camera" - "S-a părăsit spațiul" + "Cameră părăsită" + "Spațiu părăsit" "Invitația a fost refuzată" "Eroare de decriptare" "Descriere" @@ -213,6 +235,7 @@ "Fișier gol" "Criptare" "Criptare activată" + "Se termină la %1$s" "Introduceți codul PIN" "Eroare" "A apărut o eroare, este posibil să nu primiți notificări pentru mesaje noi. Vă rugăm să depanați notificările din setări. @@ -222,6 +245,7 @@ Motiv:%1$s." "Eșuat" "Favorite" "Favorită" + "Se sincronizează notificările…" "Fişier" "Fișier șters" "Fișier salvat" @@ -238,6 +262,8 @@ Motiv:%1$s." "Linie copiată în clipboard" "Linkul a fost copiat în clipboard" "Conectați un dispozitiv nou" + "Locație în timp real" + "Locația în timp real s-a încheiat" "Se încarcă…" "Se încarcă…" @@ -256,7 +282,7 @@ Motiv:%1$s." "Aspectul mesajelor" "Mesaj șters" "Modern" - "Dezactivați sunetul" + "Dezactivare sunet" "Nume" "%1$s (%2$s)" "Niciun rezultat" @@ -266,6 +292,7 @@ Motiv:%1$s." "Deconectat" "Licențe open source" "sau" + "Alte opțiuni" "Parola" "Persoane" "Permalink" @@ -284,8 +311,10 @@ Motiv:%1$s." "Se pregăteşte…" "Politica de confidențialitate" + "Privat" "Cameră privată" "Spațiu privat" + "Public" "Cameră publică" "Spațiu public" "Reacţie" @@ -293,6 +322,7 @@ Motiv:%1$s." "Motiv" "Cheie de recuperare" "Se actualizează" + "Se elimină…" "%1$d răspuns" "%1$d răspunsuri" @@ -302,6 +332,7 @@ Motiv:%1$s." "Raportați o problemă" "Raport trimis" "Editor text avansat" + "Rol" "Cameră" "Numele camerei" "de exemplu, numele proiectului dumneavoastră" @@ -318,6 +349,11 @@ Motiv:%1$s." "Securitate" "Văzut de" "Selectați un cont" + + "%1$d selectat" + "%1$d selectate" + "%1$d selectate" + "Trimiteți către" "Se trimite…" "Trimiterea a eșuat" @@ -327,13 +363,16 @@ Motiv:%1$s." "Serverul nu poate fi accesat" "Adresa URL a serverului" "Setări" - "Partajați spațiul" + "Partajare spațiu" + "Membrii noi pot vedea istoricul" + "Locație în timp real partajată" "Locație partajată" "Spațiu comun" "Eliminare în curs" "Ceva nu a mers bine" "Am întâmpinat o problemă. Vă rugăm să încercați din nou." "Spațiu" + "Membrii spațiului" "Despre ce este vorba în acest spațiu?" "%1$d Spațiu" @@ -343,11 +382,13 @@ Motiv:%1$s." "Se începe conversația…" "Autocolant" "Succes" + "Sugerat" "Sugestii" "Se sincronizează…" "Text" "Notificări despre software de la terți" "Fir" + "Fire" "Subiect" "Despre ce este vorba în această cameră?" "Nu s-a putut decripta" @@ -357,8 +398,8 @@ Motiv:%1$s." "Nu am putut trimite invitații unuia sau mai multor utilizatori." "Nu s-a putut trimite invitația (invitațiile)" "Deblocare" - "Activați sunetul" - "Apel nesuportat" + "Activare sunet" + "Apel incompatibil" "Eveniment neacceptat" "Utilizator" "Verificare anulată" @@ -378,14 +419,19 @@ Motiv:%1$s." "Mesaj vocal" "Se aşteaptă…" "Mesaj în așteptare" + "Se așteptă localizarea în timp real…" + "Oricine poate vedea istoricul" "Dumneavoastră" + "%1$s (%2$s) a distribuit acest mesaj deoarece nu erați în cameră când a fost trimis." + "%1$s a distribuit acest mesaj deoarece nu erați în cameră când a fost trimis." "Această cameră a fost configurată astfel încât noii membri să poată citi istoricul. %1$s" "Identitatea digitala lui %1$s a fost resetată. %2$s" "Identitatea digitala %2$s a lui %1$s a fost resetată. %3$s" "(%1$s)" "Identitatea lui %1$s a fost resetată." - "Identitatea %2$s a lui %1$s a fost resetată. %3$s" + "Identitatea digitală %2$s a lui %1$s a fost resetată. %3$s" "Retrageți verificarea" + "Permiteți accesul" "Linkul %1$s vă redirecționează către un alt site %2$s Sunteți sigur că doriți să continuați?" @@ -415,6 +461,7 @@ Sunteți sigur că doriți să continuați?" "%1$s nu a putut accesa locația dumneavoastră. Vă rugăm să încercați din nou mai târziu." "Trimiterea mesajului vocal nu a reușit." "Camera nu mai există sau invitația nu mai este valabilă." + "Vă rugăm să activați GPS-ul pentru a accesa funcțiile bazate pe locație." "Mesajul nu a fost găsit" "%1$s nu are permisiuni pentru a accesa locația dumneavoastră. Puteți permite accesul în Setări." "%1$s nu are permisiuni pentru a accesa locația dumneavoastră. Permiteți accesul mai jos." @@ -426,6 +473,9 @@ Sunteți sigur că doriți să continuați?" "Ne pare rău, a apărut o eroare" "🔐️ Alăturați-vă mie în %1$s" "Hei, vorbește cu mine pe %1$s: %2$s" + "Partajarea locației în timp real" + "Partajarea locației este în curs de desfășurare" + "%1$s Locație în timp real" "%1$s Android" "Rageshake pentru a raporta erori" "Captură de ecran" @@ -433,6 +483,14 @@ Sunteți sigur că doriți să continuați?" "Opțiuni" "Ștergeți %1$s" "Setări" + "Nimeni nu își partajează locația" + "Se partajează locația în timp real" + + "%1$d persoană" + "%1$d persoane" + "%1$d persoane" + + "Pe hartă" "Selectarea fișierelor media a eșuat, încercați din nou." "Apăsați pe un mesaj și alegeți \"%1$s\" pentru a-l include aici." "Fixați mesajele importante, astfel încât să poată fi descoperite cu ușurință" @@ -458,6 +516,7 @@ Sunteți sigur că doriți să continuați?" "Mesaj în %1$s" "Extindeți" "Reduceți" + "Se partajează locația în timp real" "Deja vizualizați această cameră!" "%1$s din %2$s" "%1$s Mesaje fixate" @@ -470,10 +529,14 @@ Sunteți sigur că doriți să continuați?" "Deschideți în Google Maps" "Deschideți în OpenStreetMap" "Partajați locația selectată" + "Opțiuni de partajare" "Spații pe care le-ați creat sau la care v-ați alăturat." "%1$s • %2$s" + "Creați spații pentru a organiza camerele" "Spațiu %1$s" "Spații" + "Partajat %1$s" + "Pe hartă" "Mesajul nu a fost trimis deoarece identitatea digitala verificată a lui %1$s s-a schimbat." "Mesajul nu a fost trimis deoarece %1$s nu a verificat toate dispozitivele." "Mesajul nu a fost trimis deoarece nu ați verificat unul sau mai multe dispozitive." @@ -484,5 +547,5 @@ Sunteți sigur că doriți să continuați?" "Trebuie să verificați acest dispozitiv pentru a avea acces la mesajele anterioare." "Nu aveți acces la acest mesaj" "Nu s-a putut decripta mesajul" - "Acest mesaj a fost blocat fie pentru că nu ați verificat dispozitivul, fie pentru că expeditorul trebuie să vă verifice identitatea." + "Acest mesaj a fost blocat fie pentru că nu ați verificat dispozitivul, fie pentru că expeditorul trebuie să vă verifice identitatea digitală." diff --git a/screenshots/de/features.call.impl.ui_CallScreenView_Day_3_de.png b/screenshots/de/features.call.impl.ui_CallScreenView_Day_3_de.png index bfa7251296..0996d18bf0 100644 --- a/screenshots/de/features.call.impl.ui_CallScreenView_Day_3_de.png +++ b/screenshots/de/features.call.impl.ui_CallScreenView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5988ca59d9fcfaccbf4bc2929d7c135f67be0852e739caeb566b0518fac7f75 -size 19021 +oid sha256:bc9627a6288987e446206dc54abb8e1319ba6996674f0b588decd5418eeafc1b +size 18688 diff --git a/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png b/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png new file mode 100644 index 0000000000..f889a65156 --- /dev/null +++ b/screenshots/de/features.location.api_LiveLocationSharingBanner_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efd6cb8498b29f79769b3287a062ee1ddad3c34195aa5d653b8e28e4e6f973bc +size 10767 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png index bf8a621fe1..bb93cfc575 100644 --- a/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5606179cc7dacc1d0bc86ff0f38a7654cebeb7c428575e2da51d4af0ba12b9 -size 43384 +oid sha256:1342bc5c63e6fa38797d1867b5006ebc91cfa926d189e4ff9060f84efd8146ca +size 26114 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png new file mode 100644 index 0000000000..0663eceb1d --- /dev/null +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b08870ebcd6da6372363b0af0334524a4288db7242cdb5a377512ef9aa728a66 +size 38883 diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png new file mode 100644 index 0000000000..bf8a621fe1 --- /dev/null +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a5606179cc7dacc1d0bc86ff0f38a7654cebeb7c428575e2da51d4af0ba12b9 +size 43384 diff --git a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_de.png b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_de.png index 642cb48d0f..a854d161d7 100644 --- a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebee62aecbbc7e65168799cdeb940d112095d7e38059bf91e4192dea242eb884 -size 113883 +oid sha256:ad01125850f8dc08f39832fe3ffcaae1950edd736a80f39811b4c1d9a6eafd93 +size 122264 diff --git a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_de.png b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_de.png index 0b33acb4dc..a8d7c0385c 100644 --- a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7bc6e29e43c98d47257304e38b5d6f3c0aeb55fc671adc02ea0234ba6ff31be -size 120123 +oid sha256:5dd0fa202f0d1ec868537a2726df10013072fe9cec7bce4c947ab13f39e683dc +size 122366 diff --git a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_de.png b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_de.png index 400c371f56..2ed9893cb4 100644 --- a/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aec9126323f766fa0caf0bf1aba5ebd2110bfe6076c91197f4613088eb3b8a62 -size 120233 +oid sha256:dbe9d2831b54807db3c329ac4ca9e853198195df21f8db348050b40fe98cab5f +size 121228 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png index 610bc5d0a0..d005896ab7 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2b86e1326023be378c0c03b12305810f36e4fed11886ffd3bfa46ef9e5802ab -size 18767 +oid sha256:08984e24e297742f065201c4cea3d07eda34e4c80fbcb4689e539ac64f32f1db +size 53795 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png index 98dd645229..63497b4081 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b13eb680dca1d551d843b92dff0062a58781c6c9c1fe713e35c26412b6addf6 -size 68596 +oid sha256:a7cf06d5921dae7bbdd3b774e38ac6fcbad724261c1adef9ad93ff992fcc7669 +size 51515 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png new file mode 100644 index 0000000000..98dd645229 --- /dev/null +++ b/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b13eb680dca1d551d843b92dff0062a58781c6c9c1fe713e35c26412b6addf6 +size 68596 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png index b713fac540..194a69f62b 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e08f56a74a85de1d5e792dbc17a409f95c0a72df49b3c2538814442fdd63d4a9 -size 65018 +oid sha256:cd28a4f1e30c5ef7db0cd167a3d0328af7b969ccd7c35f616472dc2ab02b5c4a +size 57867 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png index 63497b4081..b713fac540 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7cf06d5921dae7bbdd3b774e38ac6fcbad724261c1adef9ad93ff992fcc7669 -size 51515 +oid sha256:e08f56a74a85de1d5e792dbc17a409f95c0a72df49b3c2538814442fdd63d4a9 +size 65018 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png index 20df66736f..0793b01c92 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2c1dc2c23f6b8dafab3eec3bd703c9ffeacf8e3de6f0da8b182ded7dea27244 -size 53656 +oid sha256:cea139c9bc1d71d342a99ce43b6bff414e1f9cb53ac69b8994b1b7ea2c750f41 +size 60894 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png index e9d7ece4cc..c50a3e35cf 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2215fecf0a0cd2e708d7b2aba1879528d3a1ea07cc2dfa255d74986fec933ca -size 53531 +oid sha256:e19998bcd7bb878ee3fa1429ce7edd03293e8f9dfc2dc9cd439ad3a71ccd6276 +size 60764 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png index 5ed68bb027..eb4c703d89 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca1bff9d9c6f5b68823a43eebc8196ae9705f743e7b59897a69cc4016ee155ea -size 53523 +oid sha256:139462dbd69c23df0aff5b8a2b310c2a9a40a9f691ebff57a03d0bf7cf638269 +size 60757 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png index 7e71476836..4e5adc17e1 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c208bdd8a985ab7108020659f0c6dec3413fdd54a529f86d7f2bcb44448fe0d9 -size 53520 +oid sha256:3013b0a0e261f87364a5e72cce9d25cf59c2d1360dc778fec533268e1340a0ca +size 60755 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png index 46d1ca20e2..33a8e007e4 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abe747567a372eb4b674a1e7976b24d498ad2a4d7e20d425c9291ddb05922a14 -size 53339 +oid sha256:a8f056723e3999c53a1e582237290b74e178e652fd49d5b636d1256bcb846de3 +size 60597 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png index 567949e6e5..61e1649e93 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90068ee2e83b713cd11d9879bbcc3c389dd0f79778d8999df6bfc9f2b1b1bb55 -size 53662 +oid sha256:db5aa655ec48b8b8c3019e886a742a2e7922f329c7d7e7c88ca5d70cbf2f6f3e +size 60901 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png index 04837d0ca6..f48a864d6d 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3387219f505ee6168eb44879b4f943b75459f4c5b6c311cce874eba5e49ba628 -size 53108 +oid sha256:0fdcef2b4374ba9a1dded6c7fe3315ccc3c585e489d59572879a1e5efe2dff1c +size 60422 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png index 304befe3c3..3c66dd1128 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7333bd76d2de37bd13fe507d89c000984d73141a3dec81f71eaa13d6658871e -size 52600 +oid sha256:ee1b3b15e7f08f2f63c556a584db2fa3ebcf004b980f7fe837c32ea2d166159c +size 60000 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png index 025b3d596f..1eca9c9326 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97f271f0ba965c1b40edaad388e111bf624c01e5caceb73dfae1e09c28b3856a -size 61504 +oid sha256:1dca13f4ac7e2d0f80d302d2d819a32f536d5765b2d7533be4f019b6bc28dd92 +size 63485 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png index 0bf95ba4b1..246e46b33d 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:030671a61e231a9c87a221e20e63bac8add5f0a3fe4aa967313af45196151c64 -size 51196 +oid sha256:a060a4ef3ae0e7117863606fac9196536fce38d5483e3d882e86931d5f6098e1 +size 57951 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png index 8ece7cdeaa..248175a402 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c813fcb2b9cf76749d9233d5cb66e643e185ac3248eab4c4fdd71f4c7fab7ba4 -size 51057 +oid sha256:9e4faac64c7b38a8b9613eddc62805a5816ed340984e62dc5d6625a79782d3e6 +size 57811 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png index 89c8b5a536..c8bc853a8e 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66feb2ac420379ece0b85ffe53d5a25a8d11cbe27133313dd9e679d582c384fc -size 51052 +oid sha256:7e4713a87444130061d605edcf65d67d5aa1d6730e8afee1ba41c751c2dac77f +size 57804 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png index cff6d2b2f4..19d124ead1 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fd601997ff6e5af28e70c25c7c8c2a61c37e47e736b955b1510ac67e3a05034 -size 51064 +oid sha256:d8907dbe4553c7aac67cd5007dffc12a06e3c3f218fa95a75c390ee8982cf117 +size 57815 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png index fba8ff45f9..26bdb4b258 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b089d766841cc1ce24ea03d3d91b59d0f2ea1789aee3a9d06ece551c862d1ce -size 50901 +oid sha256:961a4dd2ef4c74770f9c140f4b337146225d7a626ca2082f35cc107c91263e12 +size 57670 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png index fb27f001cd..dca7c97656 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b38f2502dd31806ffab41547b66b9987de66c4e899b0df59c068d08ed4350b6 -size 51198 +oid sha256:0ea1eb184dcfcf13c8673f3df717a540c74eb500b759cd752e8fd270da9b8f4e +size 57951 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png index 0429803048..53eb657d5f 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ce5af26d03b80ad7bb0190b6b1dcdbdba4f7a666c442109da58f05c07505aad -size 50867 +oid sha256:ad17f959304774a8956cc38cb008024e79ae70dd1d2f147b2de20a95ca29ca96 +size 57638 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png index 27f6862b8c..1d51b69a1a 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:536cbfdce1cfdecdbf076f0747c50f2db7e3a88dda54c0708d4b7388c9efbf30 -size 50402 +oid sha256:85693d212e3726d4e7bc5cd3c4c2e9d73276cac7a6cba7b9549d5b45bf55b686 +size 57160 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png index 0d41a18697..5736128377 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20285a2d5b10f74f927f8459a2d92180b7f86bc266c95889b5d017679b622462 -size 58368 +oid sha256:aca6e0c7f9d7c48ca80c5a8e56a86e8d0a1cced6020d5b4ef0aae443575071ae +size 60239 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png index ab828c858a..36df3fb547 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ff37f653ce34795f7d054e1d552c161c83fe85b6c80c6925953440a75fd4ea4 -size 53181 +oid sha256:7713d5bb6252597bfc465fc85b3283e08d95684f9d0a17fc89e08f5cf8e870e4 +size 60327 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png index 6583fc352d..833794c51e 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d69ff7dc1c3efb334b8a03dbd796cde640aecac07a10adb910519810bddefed -size 53065 +oid sha256:59b93100a819cc8a0b8b8156da39f47034745bfc6d06957907009ff3eef3c2b0 +size 60210 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png index 1080a3e297..25914b7cbe 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:827f3291c252fa8a5b938ca7a38a76d0d58e1969607f2c5514d06264e10a1ddd -size 53078 +oid sha256:eaaf5ae88296f64e80ebbe60a126a1fd40944e31311329cade209631f36424cd +size 60223 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png index 1b77b7d963..2af3d0fc1b 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb8636f0a7dbad6b32346cad952b5447a79b96d3db0e5da876fca7a78ced91c2 -size 53072 +oid sha256:77c71a0c84d3fa9e39ba3c9b9b81e8a3b6e406af87e4158bfb8bb4736a8b27c7 +size 60217 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png index 3048b141f6..9f0b5c037c 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24dfd7e781f8b035045ca075b607676a2a4cbc0ef882c1f527252d85339a738e -size 52922 +oid sha256:c9228e4ace7d30444a45deb04f211782ca53c5a366e0e812145b5b772fa51d3b +size 60085 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png index 8ebc7b90cd..a880ff90f4 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:640ecec1abd4992b962a9ab8ce68d4c5a3accfde9116b93af4d55d8365c35307 -size 53187 +oid sha256:c00205667f22842c1c5d63c20c0990b6e0fb7f7cd134f625957df733627378df +size 60330 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png index c1dda2d9d5..4265684997 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc3da8b4380809f5f9724d98ba4eceb86ea9d6a7df54c4a2117d187550b46597 -size 52800 +oid sha256:cbed351c3ce6dfcb334d28316501baa5f97ade4614bd9b7dbbd91b0ce7689994 +size 59987 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png index 419eec6b72..dd801a4745 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c03b9b002d3d5a42c6a5662301e8feda20f734a299dbd753b4fa797023ad697f -size 52372 +oid sha256:09bcae54d99e6b9279794ebebf32a67f67a8347c94f3286243fe8d2c0a92f352 +size 59623 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png index 75634a5633..2e74f12057 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsViewLight_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7abc46138c98086dab38fc368d56e06ea6a434bfe61b0023a665b40e66f0aa53 -size 60801 +oid sha256:5d0ccc252fedce22f568f8d26f25a8600c21236e1d489a4dd59ccd63c1085227 +size 62771 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index eea6b4b636..08d02138ef 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -2,99 +2,99 @@ export const screenshots = [ ["en","en-dark","de",], ["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",0,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20581,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20588,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20581,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20581,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20581,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20581,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20581,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20581,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20581,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20581,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20581,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20581,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20581,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20588,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20588,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20588,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20588,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20588,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20588,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20588,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20588,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20588,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20588,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20588,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20581,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20581,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20588,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20588,], ["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",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20588,], ["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",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20581,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20581,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20581,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20581,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20581,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20581,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20581,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20581,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20581,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20581,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20588,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20588,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20588,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20588,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20588,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20588,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20588,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20588,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20588,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20581,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20581,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20581,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20581,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20581,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20588,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20588,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20588,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20588,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20588,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20581,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20581,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20581,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20581,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20588,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20588,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20588,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20588,], ["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",20581,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20588,], ["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",20581,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20588,], ["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",20581,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20588,], ["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",20581,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20588,], ["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,], @@ -104,19 +104,19 @@ 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_AttachmentsPreviewView_0_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20581,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20581,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20588,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20581,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20588,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -146,22 +146,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20581,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20588,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20581,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20581,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20588,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20581,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20581,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20581,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20581,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20581,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20588,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20588,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20588,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20588,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20588,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -172,141 +172,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20581,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20581,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20588,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20588,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20581,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20588,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20581,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20581,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20581,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20581,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20581,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20581,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20588,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20588,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20588,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20588,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20588,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20588,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20581,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20581,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20581,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20588,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20588,], ["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",20581,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20581,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20581,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20581,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20581,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20588,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20588,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20588,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20588,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20588,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20581,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20588,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20581,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20581,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20581,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20581,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20581,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20581,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20581,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20581,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20588,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20588,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20588,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20588,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20588,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20588,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20588,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20588,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20581,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20588,], ["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",20581,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20581,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20581,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20581,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20581,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20581,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20581,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20588,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20588,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20581,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20581,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20581,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20581,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20581,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20581,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20588,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20588,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20588,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20588,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20588,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20581,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20581,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20581,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20581,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20581,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20581,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20581,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20581,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20581,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20581,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20581,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20581,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20581,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20581,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20581,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20581,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20581,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20581,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20581,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20581,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20588,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20588,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20588,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20588,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20588,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20588,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20588,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20588,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20588,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20588,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20588,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20588,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20588,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20588,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20588,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20588,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20588,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20588,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20588,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20588,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20581,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20581,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20581,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20581,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20581,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20581,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20581,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20588,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20588,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20588,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20588,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20588,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20588,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20588,], ["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",20581,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20581,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20581,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20588,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20588,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20588,], ["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",20581,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20588,], ["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",20581,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20581,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20581,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20581,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20581,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20581,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20581,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20581,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20581,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20588,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20588,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20588,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20588,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20588,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20588,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20588,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20588,], ["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,], @@ -319,20 +319,20 @@ 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",20581,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20581,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20581,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20581,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20581,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20581,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20581,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20581,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20581,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20581,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20581,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20581,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20581,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20581,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20588,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20588,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20588,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20588,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20588,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20588,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20588,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20588,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20588,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20588,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20588,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20588,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20588,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20588,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -340,29 +340,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20581,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20581,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20588,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20588,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20581,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20581,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20581,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20581,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20581,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20581,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20581,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20581,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20581,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20581,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20588,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20588,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20588,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20588,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20588,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -382,49 +382,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20581,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20581,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20581,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20588,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20588,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20588,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20581,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20581,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20588,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20588,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20581,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20588,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20581,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20581,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20581,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20581,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20581,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20588,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20588,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20588,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20588,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20588,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20581,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20581,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20588,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20588,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20581,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20581,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20588,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20588,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20581,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20581,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20581,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20581,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20581,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20581,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20581,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20581,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20581,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20581,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20581,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20581,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20581,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20588,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20588,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20588,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20588,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20588,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20588,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20588,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20588,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20588,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20588,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20588,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20588,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20588,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -435,12 +435,12 @@ export const screenshots = [ ["libraries.designsystem.atomic.molecules_IconTitlePlaceholdersRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_IconTitlePlaceholdersRowMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_IconTitleSubtitleMolecule_Day_0_en","libraries.designsystem.atomic.molecules_IconTitleSubtitleMolecule_Night_0_en",0,], ["libraries.designsystem.theme.components_IconToggleButton_Toggles_en","",0,], -["appicon.enterprise_Icon_en","",0,], ["appicon.element_Icon_en","",0,], +["appicon.enterprise_Icon_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",20581,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20581,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20588,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20588,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -448,119 +448,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20581,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20588,], ["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",20581,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20588,], ["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",20581,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20581,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20588,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20588,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20581,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20581,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20588,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20581,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20581,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20581,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20588,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20588,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20581,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20581,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20581,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20588,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20581,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20581,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20581,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20588,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20588,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20588,], ["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",20581,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20581,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20581,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20581,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20581,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20588,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20588,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20588,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20581,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20581,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20588,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20588,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20581,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20581,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20581,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20588,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20588,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20581,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20581,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20581,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20581,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20581,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20581,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20588,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20581,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20588,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -613,47 +613,48 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20591,], ["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",20581,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20588,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20581,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20581,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20581,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20588,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20588,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20588,], ["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",20581,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20581,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20581,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20581,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20581,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20581,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20581,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20581,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20581,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20581,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20581,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20581,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20581,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20581,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20581,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20581,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20581,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20581,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20581,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20581,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20581,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20581,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20581,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20581,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20581,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20581,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20581,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20588,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20588,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20588,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20588,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20588,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20588,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20588,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20588,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20588,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20588,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20588,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20588,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20588,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20588,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20588,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20588,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20588,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20588,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20588,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20588,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20588,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20588,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20588,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20588,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20588,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20588,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20588,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20581,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20581,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20581,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20581,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20588,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20588,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20588,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20588,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -666,26 +667,26 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20581,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20581,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20581,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20581,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20581,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20581,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20588,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20588,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20588,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20588,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20588,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20588,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20581,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20581,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20588,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -693,10 +694,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20581,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], @@ -706,7 +707,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], @@ -716,10 +717,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20581,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], @@ -729,7 +730,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20581,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20588,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -743,7 +744,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",20581,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20588,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -752,7 +753,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20581,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20588,], ["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,], @@ -761,23 +762,24 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20581,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20581,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20581,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20581,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20581,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20581,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20581,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20581,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20581,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20581,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20581,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20581,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20581,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20581,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20581,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20588,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20588,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20588,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20588,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20588,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20588,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20591,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20588,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20588,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20588,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20588,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20588,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20588,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20588,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20588,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20588,], ["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",20581,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20588,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -788,117 +790,117 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20581,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20581,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20581,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20588,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20588,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20588,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20581,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20581,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20588,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20581,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20588,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20581,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20581,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20588,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20581,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20581,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20581,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20581,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20581,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20581,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20588,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20588,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20588,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20588,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20588,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20588,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20584,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20584,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20581,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20584,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20584,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20588,], ["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",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20581,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20581,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20581,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20581,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20581,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20581,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20588,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20588,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20588,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20588,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20588,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20581,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20581,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20581,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20581,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20581,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20588,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20588,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20588,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20588,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20588,], ["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",20581,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20581,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20581,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20581,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20581,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20581,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20581,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20581,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20581,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20581,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20581,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20588,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20588,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20588,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20588,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20588,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20588,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20588,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20588,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20588,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20588,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20588,], ["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,], @@ -912,225 +914,225 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20581,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20581,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20588,], ["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","",20581,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20581,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20588,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20588,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20581,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20581,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20581,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20581,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20581,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20581,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20581,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20581,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20581,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20581,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20581,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20581,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20581,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20581,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20581,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20581,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20588,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20588,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20588,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20588,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20588,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20588,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20588,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20588,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20588,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20588,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20588,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20588,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20588,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20588,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20588,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20588,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20581,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20581,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20588,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20588,], ["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",20581,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20581,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20581,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20581,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20581,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20581,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20581,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20588,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20588,], ["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",20581,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20581,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20581,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20581,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20581,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20581,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20581,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20581,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20581,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20581,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20581,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20581,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20581,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20581,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20581,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20581,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20581,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20588,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20588,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20588,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20588,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20588,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20588,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20588,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20588,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20588,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20588,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20588,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20588,], ["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",20581,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20581,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20581,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20581,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20581,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20588,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20588,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20588,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20588,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20588,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20581,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20581,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20588,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20588,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20581,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20581,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20581,], -["features.roomdetails.impl_RoomDetails_0_en","",20581,], -["features.roomdetails.impl_RoomDetails_10_en","",20581,], -["features.roomdetails.impl_RoomDetails_11_en","",20581,], -["features.roomdetails.impl_RoomDetails_12_en","",20581,], -["features.roomdetails.impl_RoomDetails_13_en","",20581,], -["features.roomdetails.impl_RoomDetails_14_en","",20581,], -["features.roomdetails.impl_RoomDetails_15_en","",20581,], -["features.roomdetails.impl_RoomDetails_16_en","",20581,], -["features.roomdetails.impl_RoomDetails_17_en","",20581,], -["features.roomdetails.impl_RoomDetails_18_en","",20581,], -["features.roomdetails.impl_RoomDetails_19_en","",20581,], -["features.roomdetails.impl_RoomDetails_1_en","",20581,], -["features.roomdetails.impl_RoomDetails_20_en","",20581,], -["features.roomdetails.impl_RoomDetails_21_en","",20581,], -["features.roomdetails.impl_RoomDetails_22_en","",20581,], -["features.roomdetails.impl_RoomDetails_2_en","",20581,], -["features.roomdetails.impl_RoomDetails_3_en","",20581,], -["features.roomdetails.impl_RoomDetails_4_en","",20581,], -["features.roomdetails.impl_RoomDetails_5_en","",20581,], -["features.roomdetails.impl_RoomDetails_6_en","",20581,], -["features.roomdetails.impl_RoomDetails_7_en","",20581,], -["features.roomdetails.impl_RoomDetails_8_en","",20581,], -["features.roomdetails.impl_RoomDetails_9_en","",20581,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20581,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20581,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20581,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20581,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20581,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20581,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20581,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20581,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20581,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20588,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20588,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20588,], +["features.roomdetails.impl_RoomDetails_0_en","",20588,], +["features.roomdetails.impl_RoomDetails_10_en","",20588,], +["features.roomdetails.impl_RoomDetails_11_en","",20588,], +["features.roomdetails.impl_RoomDetails_12_en","",20588,], +["features.roomdetails.impl_RoomDetails_13_en","",20588,], +["features.roomdetails.impl_RoomDetails_14_en","",20588,], +["features.roomdetails.impl_RoomDetails_15_en","",20588,], +["features.roomdetails.impl_RoomDetails_16_en","",20588,], +["features.roomdetails.impl_RoomDetails_17_en","",20588,], +["features.roomdetails.impl_RoomDetails_18_en","",20588,], +["features.roomdetails.impl_RoomDetails_19_en","",20588,], +["features.roomdetails.impl_RoomDetails_1_en","",20588,], +["features.roomdetails.impl_RoomDetails_20_en","",20588,], +["features.roomdetails.impl_RoomDetails_21_en","",20588,], +["features.roomdetails.impl_RoomDetails_22_en","",20588,], +["features.roomdetails.impl_RoomDetails_2_en","",20588,], +["features.roomdetails.impl_RoomDetails_3_en","",20588,], +["features.roomdetails.impl_RoomDetails_4_en","",20588,], +["features.roomdetails.impl_RoomDetails_5_en","",20588,], +["features.roomdetails.impl_RoomDetails_6_en","",20588,], +["features.roomdetails.impl_RoomDetails_7_en","",20588,], +["features.roomdetails.impl_RoomDetails_8_en","",20588,], +["features.roomdetails.impl_RoomDetails_9_en","",20588,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20588,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20588,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20588,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20588,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20588,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20588,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20588,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20588,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20588,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20581,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20581,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20581,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20581,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20581,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20581,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20581,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20581,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20581,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20581,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20581,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20588,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20588,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20588,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20588,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20588,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20588,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20588,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20588,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20588,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20588,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20588,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20581,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20581,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20581,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20588,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20588,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20588,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20581,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20581,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20588,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20581,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20581,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20581,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20581,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20581,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20581,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20588,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1153,16 +1155,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20581,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20588,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20581,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20588,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1171,119 +1173,119 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20581,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20581,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20581,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20588,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20588,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20588,], ["appicon.element_RoundIcon_en","",0,], ["appicon.enterprise_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20581,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20581,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20581,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20581,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20581,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20581,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20581,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20581,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20588,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20588,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20588,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20588,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20588,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20588,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20588,], ["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","",20581,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20588,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20581,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20581,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20581,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20581,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20581,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20581,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20581,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20581,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20581,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20581,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20581,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20581,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20581,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20581,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20581,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20588,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20588,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20588,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20588,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20588,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20588,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20588,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20588,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20588,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20588,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20588,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20588,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20588,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20588,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20588,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1306,35 +1308,37 @@ 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.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20581,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20581,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20581,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20581,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20581,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20581,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20581,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20581,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20581,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20581,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20581,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20581,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20581,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20581,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20581,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20588,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20588,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20588,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20588,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20588,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20588,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20588,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20588,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20588,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20588,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20588,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20588,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20588,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20588,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20588,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20591,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20591,], ["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",20581,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20581,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20581,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20581,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20581,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20581,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20581,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20581,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20581,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20581,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20581,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20588,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20588,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20588,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20588,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20588,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20588,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20588,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20588,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20588,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20588,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20588,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1344,106 +1348,106 @@ 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",20581,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20588,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20581,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20581,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20581,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20581,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20581,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20588,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20588,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20588,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20588,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20588,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20581,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20581,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20581,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20581,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20581,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20581,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20581,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20581,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20581,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20581,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20581,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20581,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20581,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20581,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20581,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20588,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20588,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20588,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20588,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20588,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20588,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20588,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20588,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20588,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20588,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20588,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20588,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20588,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20588,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20581,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20581,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20581,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20581,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20581,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20581,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20588,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20588,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20588,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20588,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20588,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20588,], ["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",20581,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20588,], ["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",20581,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20588,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20581,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20581,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20581,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20581,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20581,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20581,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20581,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20581,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20581,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20581,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20581,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20581,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20581,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20581,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20581,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20581,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20588,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20588,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20588,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20588,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20588,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20588,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20588,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20588,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20588,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20588,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20588,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20588,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20588,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20588,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20588,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20588,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20581,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20581,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20588,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20588,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1456,17 +1460,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20581,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20581,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20588,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20588,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20581,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20581,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20581,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20588,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20588,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20588,], ["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",20581,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20581,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20588,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20588,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1476,18 +1480,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20588,], ["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",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20588,], ["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,], @@ -1495,18 +1499,18 @@ 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",20581,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20588,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20581,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20581,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20588,], ["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",20581,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20588,], ["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,], @@ -1515,44 +1519,44 @@ 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",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20588,], ["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",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20588,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20588,], ["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","",20581,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20588,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20581,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20588,], ["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",20581,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20588,], ["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",20581,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20581,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20581,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20588,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20588,], ["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",20581,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20581,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20588,], ["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",20581,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20588,], ["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,], @@ -1561,8 +1565,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",20581,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20588,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20588,], ["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,], @@ -1577,8 +1581,8 @@ 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",20581,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20581,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20588,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1601,84 +1605,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20581,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20581,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20588,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20588,], ["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",20581,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20588,], ["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",20581,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20588,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20581,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20581,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20588,], ["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",20581,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20581,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20581,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20581,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20581,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20581,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20588,], ["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","",20581,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20588,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20581,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20581,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20581,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20581,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20588,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20588,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20588,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20588,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20581,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20588,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20581,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20588,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20581,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20581,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20581,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20581,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20581,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20581,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20581,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20581,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20581,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20581,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20581,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20581,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20581,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20588,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20588,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20588,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20588,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20588,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20588,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20588,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20588,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20588,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20588,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20588,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20588,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20588,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20581,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20581,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20588,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20588,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20581,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20588,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], From ef7d1a10bf1845a1202bdca9ce68c5b376970b59 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 09:17:07 +0200 Subject: [PATCH 169/471] Merge pull request #6783 from element-hq/renovate/camera Update camera to v1.6.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a8158a638f..7f6830cc7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ constraintlayout_compose = "1.1.1" lifecycle = "2.10.0" activity = "1.13.0" media3 = "1.10.0" -camera = "1.6.0" +camera = "1.6.1" work = "2.11.2" # Compose From 1c317d1ffd510c72abd5964d1e3e007cdc7b9378 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 09:21:46 +0200 Subject: [PATCH 170/471] Update dependency androidx.webkit:webkit to v1.16.0 (#6786) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f6830cc7f..62f19b0ff3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -117,7 +117,7 @@ androidx_activity_activity = { module = "androidx.activity:activity", version.re androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx_startup = "androidx.startup:startup-runtime:1.2.0" androidx_preference = "androidx.preference:preference:1.2.1" -androidx_webkit = "androidx.webkit:webkit:1.15.0" +androidx_webkit = "androidx.webkit:webkit:1.16.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_material3 = { module = "androidx.compose.material3:material3", version = '1.5.0-alpha15' } From a89d37297f69ee518d4d0da9f9179423a5501d38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 09:22:20 +0200 Subject: [PATCH 171/471] Update dependency com.google.firebase:firebase-bom to v34.13.0 (#6789) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 62f19b0ff3..442d56c349 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -80,7 +80,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:34.12.0" +google_firebase_bom = "com.google.firebase:firebase-bom:34.13.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } From e6c3a8ff1d720dd97a513fa97aabbfd327ac8f15 Mon Sep 17 00:00:00 2001 From: cizra Date: Mon, 18 May 2026 08:06:47 +0000 Subject: [PATCH 172/471] Add MIDI playback (#6770) * Add MIDI playback * Implement PR suggestions --- gradle/libs.versions.toml | 1 + libraries/mediaviewer/impl/build.gradle.kts | 1 + .../impl/local/audio/MediaAudioView.kt | 2 +- .../impl/local/player/ExoPlayerFactory.kt | 15 +++++++++++++-- .../impl/local/video/MediaVideoView.kt | 2 +- settings.gradle.kts | 2 ++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 442d56c349..831cff5052 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -111,6 +111,7 @@ androidx_media3_ui = { module = "androidx.media3:media3-ui", version.ref = "medi androidx_media3_transformer = { module = "androidx.media3:media3-transformer", version.ref = "media3" } androidx_media3_effect = { module = "androidx.media3:media3-effect", version.ref = "media3" } androidx_media3_common = { module = "androidx.media3:media3-common", version.ref = "media3" } +androidx_media3_exoplayer_midi = { module = "androidx.media3:media3-exoplayer-midi", version.ref = "media3" } androidx_biometric = "androidx.biometric:biometric-ktx:1.4.0-alpha02" androidx_activity_activity = { module = "androidx.activity:activity", version.ref = "activity" } diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index cd1579d50d..f2dbf1aacf 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(libs.coroutines.core) implementation(libs.coil.compose) implementation(libs.androidx.media3.exoplayer) + implementation(libs.androidx.media3.exoplayer.midi) implementation(libs.androidx.media3.ui) implementation(libs.telephoto.zoomableimage) implementation(libs.vanniktech.blurhash) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt index cb9d6ae9c8..3ac578196d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt @@ -88,7 +88,7 @@ fun MediaAudioView( modifier: Modifier = Modifier, isDisplayed: Boolean = true, ) { - val exoPlayer = rememberExoPlayer() + val exoPlayer = rememberExoPlayer(forAudioOnly = true) ExoPlayerMediaAudioView( isDisplayed = isDisplayed, localMediaViewState = localMediaViewState, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerFactory.kt index d0043c657a..ccd628dbc5 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/ExoPlayerFactory.kt @@ -8,14 +8,18 @@ package io.element.android.libraries.mediaviewer.impl.local.player +import androidx.annotation.OptIn import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode +import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer +@OptIn(UnstableApi::class) @Composable -fun rememberExoPlayer(): ExoPlayer { +fun rememberExoPlayer(forAudioOnly: Boolean): ExoPlayer { return if (LocalInspectionMode.current) { remember { ExoPlayerForPreview() @@ -23,7 +27,14 @@ fun rememberExoPlayer(): ExoPlayer { } else { val context = LocalContext.current remember { - ExoPlayer.Builder(context).build() + if (forAudioOnly) { + // Required for media3-exoplayer-midi to decode MIDI samples produced by DefaultExtractorsFactory. + val renderersFactory = DefaultRenderersFactory(context) + .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) + ExoPlayer.Builder(context, renderersFactory).build() + } else { + ExoPlayer.Builder(context).build() + } } } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt index 082dc0571c..9d5e290857 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt @@ -73,7 +73,7 @@ fun MediaVideoView( audioFocus: AudioFocus?, modifier: Modifier = Modifier, ) { - val exoPlayer = rememberExoPlayer() + val exoPlayer = rememberExoPlayer(forAudioOnly = false) ExoPlayerMediaVideoView( isDisplayed = isDisplayed, localMediaViewState = localMediaViewState, diff --git a/settings.gradle.kts b/settings.gradle.kts index 8c7b608465..db03a32f18 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,8 @@ dependencyResolutionManagement { url = uri("https://www.jitpack.io") content { includeModule("com.github.matrix-org", "matrix-analytics-events") + // Required transitively by androidx.media3:media3-exoplayer-midi for MIDI playback. + includeModule("com.github.philburk", "jsyn") } } google() From 2954174c5664416f9abd123d10627e5a662ed352 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 18 May 2026 10:29:14 +0200 Subject: [PATCH 173/471] Only load full media on media viewer when it's the visible item (#6794) * Only load full media on media viewer when it's the visible item * Allow cancelling ongoing media loading if scrolling fast --- .../impl/viewer/MediaViewerDataSource.kt | 22 ++++++++++++++++--- .../impl/viewer/MediaViewerEvent.kt | 1 + .../impl/viewer/MediaViewerPresenter.kt | 3 +++ .../impl/viewer/MediaViewerView.kt | 14 ++++++++---- .../impl/viewer/MediaViewerViewTest.kt | 16 ++++++++++++++ 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index eadbd544fc..01ec68a336 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaFile +import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint.MediaViewerMode import io.element.android.libraries.mediaviewer.api.local.LocalMedia @@ -40,6 +41,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import timber.log.Timber +import java.util.concurrent.ConcurrentHashMap class MediaViewerDataSource( mode: MediaViewerMode, @@ -51,7 +53,7 @@ class MediaViewerDataSource( private val pagerKeysHandler: PagerKeysHandler, ) { // List of media files that are currently being loaded - private val mediaFiles: MutableList = mutableListOf() + private val mediaFiles: ConcurrentHashMap = ConcurrentHashMap() private val galleryMode = when (mode) { MediaViewerMode.SingleMedia, @@ -69,7 +71,7 @@ class MediaViewerDataSource( fun dispose() { Timber.d("Disposing MediaViewerDataSource, closing ${mediaFiles.size} media files") - mediaFiles.forEach { it.close() } + mediaFiles.values.forEach { it.close() } mediaFiles.clear() localMediaStates.clear() } @@ -163,6 +165,12 @@ class MediaViewerDataSource( } suspend fun loadMedia(data: MediaViewerPageData.MediaViewerData) { + val currentState = localMediaStates[data.mediaSource.safeUrl]?.value + // If the media is already loading or has been loaded successfully, do nothing + if (currentState?.isLoading() == true || currentState?.isSuccess() == true) { + return + } + Timber.d("loadMedia for ${data.eventId}") val localMediaState = localMediaStates.getOrPut(data.mediaSource.safeUrl) { mutableStateOf(AsyncData.Uninitialized) @@ -175,7 +183,7 @@ class MediaViewerDataSource( filename = data.mediaInfo.filename ) .onSuccess { mediaFile -> - mediaFiles.add(mediaFile) + mediaFiles[data.mediaSource] = mediaFile } .mapCatchingExceptions { mediaFile -> localMediaFactory.createFromMediaFile( @@ -190,4 +198,12 @@ class MediaViewerDataSource( localMediaState.value = AsyncData.Failure(it) } } + + fun cancelLoadingMedia(data: MediaViewerPageData.MediaViewerData) { + if (localMediaStates[data.mediaSource.safeUrl]?.value?.isLoading() == true) { + Timber.d("cancelLoadingMedia for ${data.eventId}") + mediaFiles.remove(data.mediaSource)?.close() + localMediaStates[data.mediaSource.safeUrl]?.value = AsyncData.Uninitialized + } + } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvent.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvent.kt index 1960b69e4c..53d287075d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvent.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvent.kt @@ -29,4 +29,5 @@ sealed interface MediaViewerEvent { data class Delete(val eventId: EventId) : MediaViewerEvent data class OnNavigateTo(val index: Int) : MediaViewerEvent data class LoadMore(val direction: Timeline.PaginationDirection) : MediaViewerEvent + data class CancelLoadingMedia(val data: MediaViewerPageData.MediaViewerData) : MediaViewerEvent } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 138c73c383..d40026dd37 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -100,6 +100,9 @@ class MediaViewerPresenter( is MediaViewerEvent.LoadMedia -> { coroutineScope.downloadMedia(data = event.data) } + is MediaViewerEvent.CancelLoadingMedia -> { + dataSource.cancelLoadingMedia(event.data) + } is MediaViewerEvent.ClearLoadingError -> { dataSource.clearLoadingError(event.data) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 8bc5088187..5e9ced7d77 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.layout.onVisibilityChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource @@ -208,11 +209,16 @@ fun MediaViewerView( } is MediaViewerPageData.MediaViewerData -> { var bottomPaddingInPixels by remember { mutableIntStateOf(defaultBottomPaddingInPixels) } - LaunchedEffect(Unit) { - state.eventSink(MediaViewerEvent.LoadMedia(dataForPage)) - } Box( - modifier = Modifier.fillMaxSize() + modifier = Modifier + .onVisibilityChanged(minDurationMs = 200L) { isVisible -> + if (isVisible) { + state.eventSink(MediaViewerEvent.LoadMedia(dataForPage)) + } else { + state.eventSink(MediaViewerEvent.CancelLoadingMedia(dataForPage)) + } + } + .fillMaxSize() ) { val isDisplayed = remember(pagerState.settledPage) { // This 'item provider' lambda will be called when the data source changes with an outdated `settlePage` value diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index fdd447c4a6..b426ca50ca 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -52,6 +52,10 @@ class MediaViewerViewTest { state = state, onBackClick = callback, ) + + // Wait for enough time for the onVisibilityChanged modifier to trigger + mainClock.advanceTimeBy(200) + pressBack() } eventsRecorder.assertList( @@ -110,6 +114,10 @@ class MediaViewerViewTest { eventSink = eventsRecorder ), ) + + // Wait for enough time for the onVisibilityChanged modifier to trigger + mainClock.advanceTimeBy(200) + val contentDescription = activity!!.getString(contentDescriptionRes) onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertList( @@ -241,6 +249,10 @@ class MediaViewerViewTest { eventSink = eventsRecorder ), ) + + // Wait for enough time for the onVisibilityChanged modifier to trigger + mainClock.advanceTimeBy(200) + clickOn(CommonStrings.action_retry) eventsRecorder.assertList( listOf( @@ -263,6 +275,10 @@ class MediaViewerViewTest { eventSink = eventsRecorder ), ) + + // Wait for enough time for the onVisibilityChanged modifier to trigger + mainClock.advanceTimeBy(200) + clickOn(CommonStrings.action_cancel) eventsRecorder.assertList( listOf( From 668edbc67917bdcb50061e4b628bf3aa67c744a3 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 18 May 2026 09:50:54 +0100 Subject: [PATCH 174/471] Fix scanning code from signed out device when using "Sign in with QR code" --- .../impl/auth/RustMatrixAuthenticationService.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 6cd30d4f70..837dcee5d9 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 @@ -434,13 +434,23 @@ class RustMatrixAuthenticationService( qrCodeData: QrCodeData, ): Client { Timber.d("Creating client for QR Code login with simplified sliding sync") + // The 2025 versions of MSC4108 should always give us the baseUrl + val baseUrlOrServerName = qrCodeData.baseUrl() ?: qrCodeData.serverName() + + if (baseUrlOrServerName == null) { + // With the 2024 version of MSC4108 we treat the absence of serverName as meaning that + // the other device is not signed in. + Timber.e("The QR code is from a device that is not yet signed in") + throw HumanQrLoginException.OtherDeviceNotSignedIn() + } + return rustMatrixClientFactory .getBaseClientBuilder( sessionPaths = sessionPaths, passphrase = pendingPassphrase, slidingSyncType = ClientBuilderSlidingSync.Discovered, ) - .serverNameOrHomeserverUrl(qrCodeData.serverName()!!) + .serverNameOrHomeserverUrl(baseUrlOrServerName) .build() } From 9b1735e0e9d3e201a62a3f27d2a1483b7df44730 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 18 May 2026 11:26:58 +0200 Subject: [PATCH 175/471] Fix Maestro again after changes to the invite flow (#6796) * Fix Maestro: tap on confirmation for inviting unknown users to a room * Tap on back after inviting some user * Tap on back again * Confirm inviting someone to a DM * Make fix conditional --- .maestro/tests/roomList/createAndDeleteDM.yaml | 2 +- .maestro/tests/roomList/createAndDeleteRoom.yaml | 10 +++++++++- features/invitepeople/impl/build.gradle.kts | 1 + .../features/invitepeople/impl/InvitePeopleView.kt | 5 ++++- .../io/element/android/libraries/testtags/TestTags.kt | 2 ++ 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.maestro/tests/roomList/createAndDeleteDM.yaml b/.maestro/tests/roomList/createAndDeleteDM.yaml index a6279151ea..eed576c04f 100644 --- a/.maestro/tests/roomList/createAndDeleteDM.yaml +++ b/.maestro/tests/roomList/createAndDeleteDM.yaml @@ -7,7 +7,7 @@ appId: ${MAESTRO_APP_ID} - tapOn: text: ${MAESTRO_INVITEE1_MXID} index: 1 -- tapOn: "Send invite" +- tapOn: "Continue" - takeScreenshot: build/maestro/330-createAndDeleteDM - tapOn: "maestroelement2" - scroll diff --git a/.maestro/tests/roomList/createAndDeleteRoom.yaml b/.maestro/tests/roomList/createAndDeleteRoom.yaml index b53066ccd5..adf9d7cf29 100644 --- a/.maestro/tests/roomList/createAndDeleteRoom.yaml +++ b/.maestro/tests/roomList/createAndDeleteRoom.yaml @@ -24,8 +24,16 @@ appId: ${MAESTRO_APP_ID} text: ${MAESTRO_INVITEE2_MXID} index: 1 - tapOn: "Invite" +- runFlow: + when: + visible: 'Invite new contact to this room?' + commands: + - tapOn: + id: "confirm_invite_unknown" +# Close the keyboard if it's still open +- tapOn: "Back" +# Go back to the room details screen - tapOn: "Back" -- tapOn: "aRoomName" - scrollUntilVisible: direction: DOWN element: diff --git a/features/invitepeople/impl/build.gradle.kts b/features/invitepeople/impl/build.gradle.kts index 185497c25b..2ab2fcb4a3 100644 --- a/features/invitepeople/impl/build.gradle.kts +++ b/features/invitepeople/impl/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(projects.libraries.uiUtils) implementation(projects.libraries.androidutils) implementation(projects.libraries.usersearch.api) + implementation(projects.libraries.testtags) implementation(libs.coil.compose) implementation(projects.services.apperror.api) implementation(projects.libraries.featureflag.api) diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt index b1f139f43c..3e9de70165 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt @@ -23,6 +23,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -54,6 +55,8 @@ import io.element.android.libraries.matrix.ui.components.MatrixUserRow import io.element.android.libraries.matrix.ui.components.SelectedUsersRowList import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getBestName +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.strings.simplePluralStringResource import kotlinx.collections.immutable.ImmutableList @@ -298,7 +301,7 @@ private fun InvitePeopleConfirmModal( text = stringResource(CommonStrings.action_remove), onClick = onRemove, leadingIcon = IconSource.Vector(CompoundIcons.Close()), - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f).testTag(TestTags.confirmInviteUnknown), ) Button( text = stringResource(CommonStrings.action_invite), diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index e564ddac41..4b3a0d6ffa 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -124,4 +124,6 @@ object TestTags { * */ val roomAddressField = TestTag("room_address_field") + + val confirmInviteUnknown = TestTag("confirm_invite_unknown") } From 458405b29f7a7ee36009957967b4f1f421292e76 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 18 May 2026 11:44:47 +0100 Subject: [PATCH 176/471] Iterate --- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 837dcee5d9..336aa4d054 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 @@ -434,7 +434,8 @@ class RustMatrixAuthenticationService( qrCodeData: QrCodeData, ): Client { Timber.d("Creating client for QR Code login with simplified sliding sync") - // The 2025 versions of MSC4108 should always give us the baseUrl + // The 2025 version of MSC4108 will always give us the baseUrl and guarantees it to be a well-formed URL + // The 2024 version always has null baseUrl and uses the serverName instead, which can be null or malformed val baseUrlOrServerName = qrCodeData.baseUrl() ?: qrCodeData.serverName() if (baseUrlOrServerName == null) { @@ -444,6 +445,10 @@ class RustMatrixAuthenticationService( throw HumanQrLoginException.OtherDeviceNotSignedIn() } + if (baseUrlOrServerName.isBlank()) { + error("The QR code contains an empty base URL or server name, which is invalid") + } + return rustMatrixClientFactory .getBaseClientBuilder( sessionPaths = sessionPaths, From efa61f1d249261ea5d3c4fc39acf8a9e5169003e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 18 May 2026 11:51:23 +0100 Subject: [PATCH 177/471] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 336aa4d054..40f4b90072 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 @@ -434,8 +434,9 @@ class RustMatrixAuthenticationService( qrCodeData: QrCodeData, ): Client { Timber.d("Creating client for QR Code login with simplified sliding sync") - // The 2025 version of MSC4108 will always give us the baseUrl and guarantees it to be a well-formed URL - // The 2024 version always has null baseUrl and uses the serverName instead, which can be null or malformed + // The 2025 version of MSC4108 provides baseUrl; the 2024 version has null baseUrl and uses + // serverName instead, which can be null or malformed. We only enforce presence/non-blankness + // here and rely on serverNameOrHomeserverUrl()/the Rust builder layer to validate structure. val baseUrlOrServerName = qrCodeData.baseUrl() ?: qrCodeData.serverName() if (baseUrlOrServerName == null) { From fb17fc7e8afb326647990c9a178b494cc0c63034 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 18 May 2026 11:52:51 +0100 Subject: [PATCH 178/471] Iterate --- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 336aa4d054..dfc29ee6fe 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 @@ -446,7 +446,8 @@ class RustMatrixAuthenticationService( } if (baseUrlOrServerName.isBlank()) { - error("The QR code contains an empty base URL or server name, which is invalid") + Timber.e("The QR code contains an empty base URL or server name, which is invalid") + throw HumanQrLoginException.Unknown() } return rustMatrixClientFactory From 174a6cad0d8401e67d2516365a9a2efde06f009c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 18 May 2026 19:01:11 +0200 Subject: [PATCH 179/471] Create a new room when inviting people in a DM (#6756) * Create a new room when inviting people to a DM * Improve screenshot tests * Update screenshots --------- Co-authored-by: ElementBot --- .../android/appnav/LoggedInFlowNode.kt | 4 +- .../room/joined/JoinedRoomLoadedFlowNode.kt | 4 +- .../FakeJoinedRoomLoadedFlowNodeCallback.kt | 2 +- .../invitepeople/api/InvitePeopleEvents.kt | 1 + .../invitepeople/api/InvitePeopleState.kt | 2 + .../api/InvitePeopleStateProvider.kt | 4 ++ .../impl/DefaultInvitePeoplePresenter.kt | 48 ++++++++++++++++++- .../impl/DefaultInvitePeopleState.kt | 2 + .../impl/DefaultInvitePeopleStateProvider.kt | 3 ++ .../invitepeople/impl/InvitePeopleView.kt | 3 +- .../impl/DefaultInvitePeoplePresenterTest.kt | 48 +++++++++++++++++++ .../roomdetails/api/RoomDetailsEntryPoint.kt | 2 +- .../roomdetails/impl/RoomDetailsFlowNode.kt | 15 +++++- .../impl/RoomDetailsStateProvider.kt | 1 + .../roomdetails/impl/RoomDetailsView.kt | 34 +++++++++---- .../impl/invite/RoomInviteMembersNode.kt | 26 ++++++++++ .../impl/DefaultRoomDetailsEntryPointTest.kt | 2 +- .../roomdetails/impl/RoomDetailsViewTest.kt | 21 ++++++++ ...roomdetails.impl_RoomDetailsDark_18_en.png | 4 +- ...roomdetails.impl_RoomDetailsDark_19_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_5_en.png | 4 +- ....roomdetails.impl_RoomDetailsDark_6_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_18_en.png | 4 +- ...res.roomdetails.impl_RoomDetails_19_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_5_en.png | 4 +- ...ures.roomdetails.impl_RoomDetails_6_en.png | 4 +- 26 files changed, 220 insertions(+), 34 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index f2120038a6..47e5b7c5b3 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -382,9 +382,9 @@ class LoggedInFlowNode( } is NavTarget.Room -> { val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback { - override fun navigateToRoom(roomId: RoomId, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) { lifecycleScope.launch { - attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = false) + attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = clearBackStack) } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index febd15e9c2..a8e5921973 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -82,7 +82,7 @@ class JoinedRoomLoadedFlowNode( plugins = plugins, ), DependencyInjectionGraphOwner { interface Callback : Plugin { - fun navigateToRoom(roomId: RoomId, serverNames: List) + fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean = false) fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) fun navigateToGlobalNotificationSettings() fun navigateToDeveloperSettings() @@ -150,7 +150,7 @@ class JoinedRoomLoadedFlowNode( callback.navigateToDeveloperSettings() } - override fun navigateToRoom(roomId: RoomId, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) { callback.navigateToRoom(roomId, serverNames) } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt index 2f17071870..b0669148dd 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt @@ -13,7 +13,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.tests.testutils.lambda.lambdaError class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback { - override fun navigateToRoom(roomId: RoomId, serverNames: List) = lambdaError() + override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) = lambdaError() override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() override fun navigateToGlobalNotificationSettings() = lambdaError() override fun navigateToDeveloperSettings() = lambdaError() diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt index 264aafd570..0422fac4f1 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleEvents.kt @@ -11,4 +11,5 @@ package io.element.android.features.invitepeople.api interface InvitePeopleEvents { data object SendInvites : InvitePeopleEvents data object CloseSearch : InvitePeopleEvents + data object ClearError : InvitePeopleEvents } diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt index 9d342d191f..d14042cff7 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleState.kt @@ -9,10 +9,12 @@ package io.element.android.features.invitepeople.api import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId interface InvitePeopleState { val canInvite: Boolean val isSearchActive: Boolean val sendInvitesAction: AsyncAction + val createRoomFromDmAction: AsyncAction val eventSink: (InvitePeopleEvents) -> Unit } diff --git a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt index ce30bcc1f6..b233ed07ef 100644 --- a/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt +++ b/features/invitepeople/api/src/main/kotlin/io/element/android/features/invitepeople/api/InvitePeopleStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.invitepeople.api import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.RoomId class InvitePeopleStateProvider : PreviewParameterProvider { override val values: Sequence @@ -25,6 +26,7 @@ private data class PreviewInvitePeopleState( override val canInvite: Boolean, override val isSearchActive: Boolean, override val sendInvitesAction: AsyncAction, + override val createRoomFromDmAction: AsyncAction, override val eventSink: (InvitePeopleEvents) -> Unit, ) : InvitePeopleState @@ -32,10 +34,12 @@ private fun aPreviewInvitePeopleState( canInvite: Boolean = false, isSearchActive: Boolean = false, sendInvitesAction: AsyncAction = AsyncAction.Uninitialized, + createRoomFromDmAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (InvitePeopleEvents) -> Unit = {}, ) = PreviewInvitePeopleState( canInvite = canInvite, isSearchActive = isSearchActive, sendInvitesAction = sendInvitesAction, + createRoomFromDmAction = createRoomFromDmAction, eventSink = eventSink ) diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt index b223d30617..44627daca3 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenter.kt @@ -38,12 +38,17 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.annotations.SessionCoroutineScope 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 +import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.filterMembers +import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility +import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.recent.getRecentDirectRooms +import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.usersearch.api.UserRepository @@ -88,6 +93,7 @@ class DefaultInvitePeoplePresenter( var searchActive by rememberSaveable { mutableStateOf(false) } val showSearchLoader = rememberSaveable { mutableStateOf(false) } val sendInvitesAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } + val createRoomFromDmAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val recentDirectRooms by produceState(emptyList(), roomMembers.value) { if (roomMembers.value.isSuccess()) { @@ -208,7 +214,13 @@ class DefaultInvitePeoplePresenter( ) } else { room.dataOrNull()?.let { - sessionCoroutineScope.sendInvites(it, selectedUsers.value, sendInvitesAction) + sessionCoroutineScope.launch { + if (it.isDm()) { + createRoomFromDm(it, selectedUsers.value, createRoomFromDmAction) + } else { + sendInvites(it, selectedUsers.value, sendInvitesAction) + } + } } } } @@ -216,6 +228,10 @@ class DefaultInvitePeoplePresenter( searchActive = false queryState.clearText() } + is InvitePeopleEvents.ClearError -> { + sendInvitesAction.value = AsyncAction.Uninitialized + createRoomFromDmAction.value = AsyncAction.Uninitialized + } } } @@ -228,6 +244,7 @@ class DefaultInvitePeoplePresenter( searchResults = searchResults.value, showSearchLoader = showSearchLoader.value, sendInvitesAction = sendInvitesAction.value, + createRoomFromDmAction = createRoomFromDmAction.value, suggestions = suggestions, eventSink = ::handleEvent, ) @@ -254,6 +271,35 @@ class DefaultInvitePeoplePresenter( } } + private fun CoroutineScope.createRoomFromDm( + currentRoom: JoinedRoom, + selectedUsers: List, + createRoomFromDmAction: MutableState>, + ) = launch { + createRoomFromDmAction.runUpdatingState { + val currentUsers = currentRoom.getMembers(limit = 100).getOrNull().orEmpty() + .filter { it.membership.isActive() } + val invitees = (currentUsers.map { it.userId } + selectedUsers.map { it.userId }) + .filter { it != matrixClient.sessionId } + .distinct() + matrixClient.createRoom( + CreateRoomParameters( + name = null, + topic = null, + isEncrypted = true, + isDirect = false, + visibility = RoomVisibility.Private, + preset = RoomPreset.PRIVATE_CHAT, + invite = invitees, + avatar = null, + joinRuleOverride = JoinRule.Invite, + historyVisibilityOverride = RoomHistoryVisibility.Invited, + isSpace = false, + ) + ) + } + } + @JvmName("toggleUserInSelectedUsers") private fun MutableState>.toggleUser(user: MatrixUser) { value = if (value.contains(user)) { diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt index 842bcf1148..46e5d9f1a5 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleState.kt @@ -14,6 +14,7 @@ import io.element.android.features.invitepeople.api.InvitePeopleState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList @@ -26,6 +27,7 @@ data class DefaultInvitePeopleState( val selectedUsers: ImmutableList, override val isSearchActive: Boolean, override val sendInvitesAction: AsyncAction, + override val createRoomFromDmAction: AsyncAction, val suggestions: ImmutableList, override val eventSink: (InvitePeopleEvents) -> Unit ) : InvitePeopleState diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt index 45b47fbc6e..93a6e03bd3 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeopleStateProvider.kt @@ -18,6 +18,7 @@ import io.element.android.libraries.designsystem.preview.USER_NAME_CAROL import io.element.android.libraries.designsystem.preview.USER_NAME_EVE import io.element.android.libraries.designsystem.preview.USER_NAME_JUSTIN import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList @@ -119,6 +120,7 @@ private fun aDefaultInvitePeopleState( isSearchActive: Boolean = false, showSearchLoader: Boolean = false, sendInvitesAction: AsyncAction = AsyncAction.Uninitialized, + createRoomFromDmAction: AsyncAction = AsyncAction.Uninitialized, suggestions: List = aMatrixUserList() .take(5) .map { user -> anInvitableUser(matrixUser = user, isSelected = user in selectedUsers) }, @@ -132,6 +134,7 @@ private fun aDefaultInvitePeopleState( isSearchActive = isSearchActive, showSearchLoader = showSearchLoader, sendInvitesAction = sendInvitesAction, + createRoomFromDmAction = createRoomFromDmAction, suggestions = suggestions.toImmutableList(), eventSink = {}, ) diff --git a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt index 3e9de70165..2bbd64c977 100644 --- a/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt +++ b/features/invitepeople/impl/src/main/kotlin/io/element/android/features/invitepeople/impl/InvitePeopleView.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -105,7 +106,7 @@ private fun InvitePeopleContentView( } InvitePeopleSearchBar( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.imePadding().fillMaxWidth(), queryState = state.searchQuery, showLoader = state.showSearchLoader, selectedUsers = state.selectedUsers, diff --git a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt index 5d5a533bb6..4e141b2c4d 100644 --- a/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt +++ b/features/invitepeople/impl/src/test/kotlin/io/element/android/features/invitepeople/impl/DefaultInvitePeoplePresenterTest.kt @@ -831,6 +831,54 @@ internal class DefaultInvitePeoplePresenterTest { } } + @Test + fun `present - inviting someone to a DM creates a new room`() = runTest { + val alice = aMatrixUser("@alice:example.com") + + val matrixClient = FakeMatrixClient( + encryptionService = FakeEncryptionService( + getUserIdentityResult = lambdaRecorder { userId: UserId -> + Result.success(IdentityState.Pinned) + } + ) + ) + val presenter = createDefaultInvitePeoplePresenter( + coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), + matrixClient = matrixClient, + joinedRoom = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + initialRoomInfo = aRoomInfo(isDm = true), + getMembersResult = { Result.success(listOf(aRoomMember(userId = alice.userId, membership = RoomMembershipState.JOIN))) }, + ) + ) + ) + presenter.test { + val initialState = awaitItem() + skipItems(1) + + // We want to add a new user to a DM + initialState.eventSink(DefaultInvitePeopleEvents.ToggleUser(alice)) + + // And we send the invites + initialState.eventSink(InvitePeopleEvents.SendInvites) + + skipItems(1) + + awaitItemAsDefault().run { + assertThat(canInvite).isTrue() + assertThat(sendInvitesAction.isUninitialized()).isTrue() + // Inviting to a DM should trigger the creation of a new room + assertThat(createRoomFromDmAction.isLoading()).isTrue() + } + + awaitItemAsDefault().run { + assertThat(sendInvitesAction.isUninitialized()).isTrue() + // Once the room is created, the action should be successful + assertThat(createRoomFromDmAction.isSuccess()).isTrue() + } + } + } + private suspend fun FakeUserRepository.emitStateWithUsers( users: List, isSearching: Boolean = false diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index 07e10c65ef..bbaac0cbc1 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -40,7 +40,7 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun navigateToGlobalNotificationSettings() fun navigateToDeveloperSettings() - fun navigateToRoom(roomId: RoomId, serverNames: List) + fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean = false) fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) fun startForwardEventFlow(eventId: EventId, fromPinnedEvents: Boolean) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 818287ab68..4cf5056b4e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -263,7 +263,20 @@ class RoomDetailsFlowNode( } NavTarget.InviteMembers -> { - createNode(buildContext) + val callback = object : RoomInviteMembersNode.Callback { + override fun openCreatedRoom(roomId: RoomId) { + navigateUp() + room.roomCoroutineScope.launch { + callback.navigateToRoom( + roomId = roomId, + serverNames = emptyList(), + // Remove the invite screen from the backstack to avoid navigating back to it after the new room has been created + clearBackStack = true, + ) + } + } + } + createNode(buildContext, plugins = listOf(callback)) } is NavTarget.RoomNotificationSettings -> { 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 ad627d8677..b569527d9c 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 @@ -180,6 +180,7 @@ fun aDmRoomDetailsState( roomName = roomName, isPublic = false, isEncrypted = isEncrypted, + canInvite = true, roomType = RoomDetailsType.Dm(otherMember = aDmRoomMember(isIgnored = isDmMemberIgnored)), roomMemberDetailsState = aUserProfileState( isBlocked = AsyncData.Success(isDmMemberIgnored), 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 e877c24554..cf5265b145 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 @@ -208,8 +208,15 @@ fun RoomDetailsView( onClick = onSecurityAndPrivacyClick ) } + } - state.roomMemberDetailsState?.let { dmMemberDetails -> + state.roomMemberDetailsState?.let { dmMemberDetails -> + if (state.canInvite) { + PreferenceCategory { + InviteItem(onClick = invitePeople) + } + } + PreferenceCategory { ProfileItem( verificationState = dmMemberDetails.verificationState, onClick = { onProfileClick(dmMemberDetails.userId) } @@ -374,14 +381,14 @@ private fun MainActionsSection( onClick = { onCall(CallIntent.VIDEO) }, ) } + if (state.canInvite && state.roomType !is RoomDetailsType.Dm) { + MainActionButton( + title = stringResource(CommonStrings.action_invite), + imageVector = CompoundIcons.UserAdd(), + onClick = onInvitePeople, + ) + } if (state.roomType is RoomDetailsType.Room) { - if (state.canInvite) { - MainActionButton( - title = stringResource(CommonStrings.action_invite), - imageVector = CompoundIcons.UserAdd(), - onClick = onInvitePeople, - ) - } // Share CTA should be hidden for DMs MainActionButton( title = stringResource(CommonStrings.action_share), @@ -693,6 +700,17 @@ private fun MembersItem( ) } +@Composable +private fun InviteItem( + onClick: () -> Unit, +) { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_details_invite_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserAdd())), + onClick = onClick, + ) +} + @Composable private fun PinnedMessagesItem( pinnedMessagesCount: Int?, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt index ea0ed1bb72..3919817313 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersNode.kt @@ -11,6 +11,7 @@ package io.element.android.features.roomdetails.impl.invite import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -19,10 +20,16 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.features.invitepeople.api.InvitePeopleEvents import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleRenderer +import io.element.android.libraries.architecture.callback +import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) @@ -35,6 +42,10 @@ class RoomInviteMembersNode( room: JoinedRoom, invitePeoplePresenterFactory: InvitePeoplePresenter.Factory, ) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun openCreatedRoom(roomId: RoomId) + } + init { lifecycle.subscribe( onResume = { @@ -48,6 +59,8 @@ class RoomInviteMembersNode( roomId = room.roomId, ) + private val callback = plugins.callback() + @Composable override fun View(modifier: Modifier) { val state = invitePeoplePresenter.present() @@ -59,6 +72,19 @@ class RoomInviteMembersNode( } } + AsyncActionView( + async = state.createRoomFromDmAction, + onSuccess = { roomId -> + callback.openCreatedRoom(roomId) + }, + progressDialog = { + ProgressDialog(text = stringResource(CommonStrings.common_creating_room)) + }, + onErrorDismiss = { + state.eventSink(InvitePeopleEvents.ClearError) + } + ) + RoomInviteMembersView( state = state, modifier = modifier, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index bcf25b2aac..597cc82f7d 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -70,7 +70,7 @@ class DefaultRoomDetailsEntryPointTest { val callback = object : RoomDetailsEntryPoint.Callback { override fun navigateToGlobalNotificationSettings() = lambdaError() override fun navigateToDeveloperSettings() = lambdaError() - override fun navigateToRoom(roomId: RoomId, serverNames: List) = lambdaError() + override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) = lambdaError() override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() override fun startForwardEventFlow(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 5fe0130c73..9494df5d44 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -13,6 +13,8 @@ package io.element.android.features.roomdetails.impl import androidx.activity.ComponentActivity import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick @@ -339,6 +341,25 @@ class RoomDetailsViewTest { clickOn(R.string.screen_room_details_profile_row_title) } } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on invite invokes the expected callback`() = runAndroidComposeUiTest { + ensureCalledOnce { callback -> + setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + roomType = RoomDetailsType.Dm( + aDmRoomMember(userId = UserId("@other:local.org")), + ), + roomMemberDetailsState = aUserProfileState(userId = A_USER_ID), + canInvite = true, + ), + invitePeople = callback, + ) + onAllNodesWithText(activity!!.getString(R.string.screen_room_details_invite_title)).onLast().performClick() + } + } } private fun AndroidComposeUiTest.setRoomDetailView( diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png index ddcd78552d..9f56dab8f3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7b9375965be172b7a1a6b00be38c66dc5f73d7d76f6b07fd1cb8defbadae840 -size 41554 +oid sha256:2f239cb428d2e4cffa86e8d8397904af0c0c5e621585f31bcf3135cdd2c81a40 +size 40541 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png index 669af01562..81bb7961e9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7890cc2fc8e722bfdee5e1c0cdda335c386a2e70a918b710a44a788457dd8497 -size 41507 +oid sha256:3bcc557108fc16a1f63d22b3512d271d30dccc801838607e78921baf9ba490c0 +size 40509 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png index 31daf341c2..2d3d2f0a74 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:105399c3ca12de9e911ccf7b4aaebe87bffeba45b1740d067b5b8e67c5647952 -size 41196 +oid sha256:76f63f6c98318762a574d7fb04c8e23ad8312b1fc9adc404669ac6d9e2c42097 +size 40127 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png index 3309d53220..bca90757f7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5da1fef365731946e08a38250187fe6f3108aff466d7771863c0ba52fb5b7728 -size 42254 +oid sha256:3063251e98ea6e797e5178cb954d99e71fc1422df19c24a59b1395531380e941 +size 41113 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png index 74405c8661..3151e8d088 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fef392cb892bf6dc746ed59f1279bf9558536ef439715d1834bd88260739949d -size 42441 +oid sha256:6fb58a8db96c0e76d5a0e8c8d2a38ae206e3dade479bd924fd404902c8e69b42 +size 41322 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png index dd6c6a993d..e213e801f3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9b7c26c5f0638dbe7731e239fc16bb2ba4330986af7218688a3a12ede2bfd9d -size 42313 +oid sha256:5030731135dec08034e1992499caa251aae8039cebafb09210fc83d622b06bd9 +size 41257 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png index e6cae695cf..5c16501c76 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d6a5102a9dd44a6a15dc6b40ec422a115a302506a2b3808a1df84ca34cb323e -size 41972 +oid sha256:e54b946de1128b1109ece7553e73d7676199edc45e7f275014c37503336d302e +size 40862 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png index a1947b9c2c..55ab231ede 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1538aa1f1313df1f6cbbc0ed7ef92d264c85b7de0080492a7cba19bbd235131d -size 43100 +oid sha256:4f3df6d78498d2b390cf926fb1111fdfa0f38ba5dd9e023b9bd2af42c3951511 +size 41921 From 828337b343ee8493663378b7c0e2707db01590e7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 18 May 2026 20:25:36 +0200 Subject: [PATCH 180/471] Remove LiveLocationSharing feature flag --- .../impl/share/ShareLocationPresenter.kt | 8 +---- .../DefaultShareLocationEntryPointTest.kt | 2 -- .../impl/share/ShareLocationPresenterTest.kt | 21 +------------ .../advanced/AdvancedSettingsPresenter.kt | 14 +-------- .../advanced/AdvancedSettingsPresenterTest.kt | 30 ++----------------- .../libraries/featureflag/api/FeatureFlags.kt | 7 ----- 6 files changed, 5 insertions(+), 77 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index 6c0d2120bc..a1e45cfea2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -40,8 +40,6 @@ import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.dateformatter.api.DurationFormatter -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.room.CreateTimelineParams import io.element.android.libraries.matrix.api.room.JoinedRoom @@ -66,7 +64,6 @@ class ShareLocationPresenter( private val messageComposerContext: MessageComposerContext, private val locationActions: LocationActions, private val buildMeta: BuildMeta, - private val featureFlagService: FeatureFlagService, private val client: MatrixClient, private val durationFormatter: DurationFormatter, private val liveLocationShareManager: ActiveLiveLocationShareManager, @@ -83,9 +80,6 @@ class ShareLocationPresenter( override fun present(): ShareLocationState { val permissionsState: PermissionsState = permissionsPresenter.present() var trackUserPosition: Boolean by remember { mutableStateOf(permissionsState.isAnyGranted && locationActions.isLocationEnabled()) } - val isLiveLocationSharingEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.LiveLocationSharing) - }.collectAsState(false) val appName by remember { derivedStateOf { buildMeta.applicationName } } var dialogState: ShareLocationState.Dialog by remember { mutableStateOf(ShareLocationState.Dialog.None) @@ -171,7 +165,7 @@ class ShareLocationPresenter( dialogState = dialogState, trackUserLocation = trackUserPosition, hasLocationPermission = permissionsState.isAnyGranted, - canShareLiveLocation = isLiveLocationSharingEnabled && timelineMode.canShareLiveLocation(), + canShareLiveLocation = timelineMode.canShareLiveLocation(), appName = appName, startLiveLocationAction = startLiveLocationAction.value, eventSink = ::handleEvent, diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt index 9b1a14fb51..6f20e296d9 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt @@ -17,7 +17,6 @@ import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.dateformatter.test.FakeDurationFormatter -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -50,7 +49,6 @@ class DefaultShareLocationEntryPointTest { messageComposerContext = FakeMessageComposerContext(), locationActions = FakeLocationActions(), buildMeta = aBuildMeta(), - featureFlagService = FakeFeatureFlagService(), client = FakeMatrixClient(), durationFormatter = FakeDurationFormatter(), liveLocationShareManager = FakeActiveLiveLocationShareManager(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index dff5f05ee9..1bc4fce586 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -29,8 +29,6 @@ import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.dateformatter.test.FakeDurationFormatter -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId @@ -77,7 +75,6 @@ class ShareLocationPresenterTest { private val fakeMessageComposerContext = FakeMessageComposerContext() private val fakeLocationActions = FakeLocationActions() private val fakeBuildMeta = aBuildMeta(applicationName = "app name") - private val fakeFeatureFlagService = FakeFeatureFlagService() private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID) private val durationFormatter = FakeDurationFormatter() @@ -96,7 +93,6 @@ class ShareLocationPresenterTest { messageComposerContext = fakeMessageComposerContext, locationActions = locationActions, buildMeta = fakeBuildMeta, - featureFlagService = fakeFeatureFlagService, client = fakeMatrixClient, durationFormatter = durationFormatter, liveLocationShareManager = liveLocationShareManager, @@ -658,21 +654,7 @@ class ShareLocationPresenterTest { } @Test - fun `canShareLiveLocation is false when the feature is disabled`() = runTest { - fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, false) - val shareLocationPresenter = createShareLocationPresenter( - timelineMode = Timeline.Mode.Live, - ) - shareLocationPresenter.test { - skipItems(1) - val state = awaitItem() - assertThat(state.canShareLiveLocation).isFalse() - } - } - - @Test - fun `canShareLiveLocation is true when the feature is enabled`() = runTest { - fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) + fun `canShareLiveLocation is true in live timeline`() = runTest { val shareLocationPresenter = createShareLocationPresenter( timelineMode = Timeline.Mode.Live, ) @@ -685,7 +667,6 @@ class ShareLocationPresenterTest { @Test fun `canShareLiveLocation is false in thread timeline`() = runTest { - fakeFeatureFlagService.setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) val shareLocationPresenter = createShareLocationPresenter( timelineMode = Timeline.Mode.Thread(A_THREAD_ID), ) 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 3d1fb3b0c7..45c136ab62 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 @@ -25,11 +25,8 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch @Inject @@ -56,17 +53,8 @@ class AdvancedSettingsPresenter( appPreferencesStore.getThemeFlow().mapToTheme(isBlackThemeAllowed) }.collectAsState(initial = Theme.System) - @OptIn(ExperimentalCoroutinesApi::class) val liveLocationMinimumDistanceUpdate by produceState(null) { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.LiveLocationSharing) - .flatMapLatest { isEnabled -> - if (isEnabled) { - appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow() - } else { - emptyFlow() - } - } - .collect { value = it } + appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow().collect { value = it } } val mediaPreviewConfigState = mediaPreviewConfigStateStore.state() 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 27d91bd8de..25d7c01778 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 @@ -210,35 +210,12 @@ class AdvancedSettingsPresenterTest { } } - @Test - fun `present - live location minimum distance is null when feature is disabled`() = runTest { - val appPreferencesStore = InMemoryAppPreferencesStore( - liveLocationMinimumDistanceUpdate = 50, - ) - val featureFlagService = FakeFeatureFlagService().apply { - setFeatureEnabled(FeatureFlags.LiveLocationSharing, false) - } - val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) - - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - skipItems(1) - with(awaitItem()) { - assertThat(liveLocationMinimumDistanceUpdate).isNull() - } - } - } - @Test fun `present - exposes live location minimum distance from app preferences`() = runTest { val appPreferencesStore = InMemoryAppPreferencesStore( liveLocationMinimumDistanceUpdate = 50, ) - val featureFlagService = FakeFeatureFlagService().apply { - setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) - } - val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) + val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -256,10 +233,7 @@ class AdvancedSettingsPresenterTest { val appPreferencesStore = InMemoryAppPreferencesStore( liveLocationMinimumDistanceUpdate = 10, ) - val featureFlagService = FakeFeatureFlagService().apply { - setFeatureEnabled(FeatureFlags.LiveLocationSharing, true) - } - val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore, featureFlagService = featureFlagService) + val presenter = createAdvancedSettingsPresenter(appPreferencesStore = appPreferencesStore) moleculeFlow(RecompositionMode.Immediate) { presenter.present() 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 ed8916c8b4..097df99800 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 @@ -100,13 +100,6 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), - LiveLocationSharing( - key = "feature.liveLocationSharing", - title = "Live location sharing", - description = "Allow sharing live location in rooms.", - defaultValue = { false }, - isFinished = false, - ), ValidateNetworkWhenSchedulingNotificationFetching( key = "feature.validate_network_when_scheduling_notification_fetching", title = "Validate internet connectivity when scheduling notification fetching", From 4e3853a7188fa53382dee4590f45169e3f115e8c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 18 May 2026 22:18:52 +0200 Subject: [PATCH 181/471] Attempt to fix room list item duplicates at midnight (#6793) * Attempt to fix room list item duplicates at midnight This seems to happen because of a race condition between `RoomListDataSource.observeDateTimeChanges` and `RoomListDataSource.replaceWith` being called at almost the same time and the first one using the newly received items from observing the timeline items but not updating the cache which will be later reused by `replaceWith`, containing incorrect indices --- .../impl/datasource/RoomListDataSource.kt | 5 +- .../impl/datasource/RoomListDataSourceTest.kt | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSource.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSource.kt index 3ff4339bb2..56b2c1ade4 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSource.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSource.kt @@ -83,8 +83,8 @@ class RoomListDataSource( val loadingState = roomList.loadingState - fun launchIn(coroutineScope: CoroutineScope) { - roomList + fun launchIn(coroutineScope: CoroutineScope): Job { + return roomList .summaries .onEach { roomSummaries -> replaceWith(roomSummaries) @@ -212,6 +212,7 @@ class RoomListDataSource( private suspend fun rebuildAllRoomSummaries() { lock.withLock { roomList.summaries.replayCache.firstOrNull()?.let { roomSummaries -> + diffCacheUpdater.updateWith(roomSummaries) buildAndEmitAllRooms(roomSummaries, useCache = false) } } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt index ace1ef2eaa..6df5e05df5 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/datasource/RoomListDataSourceTest.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 +import io.element.android.libraries.matrix.test.A_ROOM_ID_3 import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.roomlist.FakeDynamicRoomList @@ -197,6 +198,68 @@ class RoomListDataSourceTest { } } + @Test + fun `regression test for race with DateTimeObserver and new items`() = runTest { + val roomList = FakeDynamicRoomList(summaries = MutableStateFlow(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2)))) + val roomListService = FakeRoomListService( + createRoomListLambda = { roomList } + ).apply { + postState(RoomListService.State.Running) + } + val dateTimeObserver = FakeDateTimeObserver() + var dateFormatterResult = "Today" + val dateFormatter = FakeDateFormatter({ _, _, _ -> dateFormatterResult }) + val roomListDataSource = createRoomListDataSource( + roomListService = roomListService, + roomListRoomSummaryFactory = aRoomListRoomSummaryFactory( + dateFormatter = dateFormatter, + ), + dateTimeObserver = dateTimeObserver, + ) + roomListDataSource.roomSummariesFlow.test { + // Observe room list items changes + val job = roomListDataSource.launchIn(backgroundScope) + // Get the initial room list + val initialRoomList = awaitItem() + assertThat(initialRoomList).hasSize(2) + assertThat(initialRoomList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(initialRoomList[0].timestamp).isEqualTo(dateFormatterResult) + assertThat(initialRoomList[1].roomId).isEqualTo(A_ROOM_ID_2) + assertThat(initialRoomList[1].timestamp).isEqualTo(dateFormatterResult) + + // Stop processing room list updates so we can force a race condition with the date time observer updates + job.cancel() + + // Trigger a date change and a new item at the same time + dateFormatterResult = "Yesterday" + roomList.summaries.tryEmit(listOf(aRoomSummary(roomId = A_ROOM_ID), aRoomSummary(roomId = A_ROOM_ID_3), aRoomSummary(roomId = A_ROOM_ID_2))) + dateTimeObserver.given(DateTimeObserver.Event.DateChanged(Instant.MIN, Instant.now())) + + // The race condition would have caused the cache indices to be corrupted and only 2 items would be emitted + val rebuiltRoomList = awaitItem() + assertThat(rebuiltRoomList).hasSize(3) + assertThat(rebuiltRoomList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(rebuiltRoomList[0].timestamp).isEqualTo(dateFormatterResult) + assertThat(rebuiltRoomList[1].roomId).isEqualTo(A_ROOM_ID_3) + assertThat(rebuiltRoomList[1].timestamp).isEqualTo(dateFormatterResult) + assertThat(rebuiltRoomList[2].roomId).isEqualTo(A_ROOM_ID_2) + assertThat(rebuiltRoomList[2].timestamp).isEqualTo(dateFormatterResult) + + // Restart processing room list updates + roomListDataSource.launchIn(backgroundScope) + + // Check there is a new list and it's not the same as the previous one + val newRoomList = awaitItem() + assertThat(newRoomList).hasSize(3) + assertThat(newRoomList[0].roomId).isEqualTo(A_ROOM_ID) + assertThat(newRoomList[0].timestamp).isEqualTo(dateFormatterResult) + assertThat(newRoomList[1].roomId).isEqualTo(A_ROOM_ID_3) + assertThat(newRoomList[1].timestamp).isEqualTo(dateFormatterResult) + assertThat(newRoomList[2].roomId).isEqualTo(A_ROOM_ID_2) + assertThat(newRoomList[2].timestamp).isEqualTo(dateFormatterResult) + } + } + private fun TestScope.createRoomListDataSource( roomListService: FakeRoomListService = FakeRoomListService(), roomListRoomSummaryFactory: RoomListRoomSummaryFactory = aRoomListRoomSummaryFactory(), From e3d1a811d5862639b5c3f2c81fdc79dddd4710b4 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 18 May 2026 22:19:22 +0200 Subject: [PATCH 182/471] Disable biometric unlock when we disable pin code unlock (#6781) * Disable biometric unlock when we disable pin code unlock --- .../lockscreen/impl/DefaultLockScreenService.kt | 3 ++- .../impl/biometric/BiometricAuthenticatorManager.kt | 5 +++++ .../biometric/DefaultBiometricAuthenticatorManager.kt | 10 ++++++---- .../impl/settings/LockScreenSettingsPresenter.kt | 1 + .../biometric/FakeBiometricAuthenticatorManager.kt | 5 +++++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt index 7ac42feda6..49d299b3f9 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenService.kt @@ -43,7 +43,7 @@ class DefaultLockScreenService( private val coroutineScope: CoroutineScope, private val sessionObserver: SessionObserver, private val appForegroundStateService: AppForegroundStateService, - biometricAuthenticatorManager: BiometricAuthenticatorManager, + private val biometricAuthenticatorManager: BiometricAuthenticatorManager, ) : LockScreenService { private val _lockState = MutableStateFlow(LockScreenLockState.Unlocked) override val lockState: StateFlow = _lockState @@ -81,6 +81,7 @@ class DefaultLockScreenService( override suspend fun onSessionDeleted(userId: String, wasLastSession: Boolean) { if (wasLastSession) { pinCodeManager.deletePinCode() + biometricAuthenticatorManager.disable() } } }) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticatorManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticatorManager.kt index 9917845725..2ea0ed7d05 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticatorManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/BiometricAuthenticatorManager.kt @@ -24,6 +24,11 @@ interface BiometricAuthenticatorManager { fun addCallback(callback: BiometricAuthenticator.Callback) fun removeCallback(callback: BiometricAuthenticator.Callback) + /** + * Disable using the biometric unlock feature and remove any data associated with it. + */ + suspend fun disable() + /** * Remember a biometric authenticator ready for unlocking the app. */ diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricAuthenticatorManager.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricAuthenticatorManager.kt index 8bb044fd06..117323ec0a 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricAuthenticatorManager.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/biometric/DefaultBiometricAuthenticatorManager.kt @@ -80,10 +80,7 @@ class DefaultBiometricAuthenticatorManager( private val internalCallback = object : DefaultBiometricUnlockCallback() { override fun onBiometricSetupError() { - coroutineScope.launch { - lockScreenStore.setIsBiometricUnlockAllowed(false) - secretKeyRepository.deleteKey(SECRET_KEY_ALIAS) - } + coroutineScope.launch { disable() } } } @@ -120,6 +117,11 @@ class DefaultBiometricAuthenticatorManager( ) } + override suspend fun disable() { + lockScreenStore.setIsBiometricUnlockAllowed(false) + secretKeyRepository.deleteKey(SECRET_KEY_ALIAS) + } + @Composable private fun rememberBiometricAuthenticator( isAvailable: Boolean, diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt index 83a0253f39..17a0213f63 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsPresenter.kt @@ -59,6 +59,7 @@ class LockScreenSettingsPresenter( if (showRemovePinConfirmation) { showRemovePinConfirmation = false pinCodeManager.deletePinCode() + biometricAuthenticatorManager.disable() } } } diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticatorManager.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticatorManager.kt index 9e9b892582..0ae8552334 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticatorManager.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/biometric/FakeBiometricAuthenticatorManager.kt @@ -15,6 +15,7 @@ class FakeBiometricAuthenticatorManager( override var isDeviceSecured: Boolean = true, override var hasAvailableAuthenticator: Boolean = false, private val createBiometricAuthenticator: () -> BiometricAuthenticator = { FakeBiometricAuthenticator() }, + private val disableLambda: suspend () -> Unit = { }, ) : BiometricAuthenticatorManager { override fun addCallback(callback: BiometricAuthenticator.Callback) { // no-op @@ -37,4 +38,8 @@ class FakeBiometricAuthenticatorManager( createBiometricAuthenticator() } } + + override suspend fun disable() { + disableLambda() + } } From c94ba068ed28b47ac7b0fa3636c7e9bb56404bb6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 07:18:13 +0000 Subject: [PATCH 183/471] Update dependencyAnalysis to v3.11.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 831cff5052..8be68a0066 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,7 +51,7 @@ telephoto = "0.19.0" haze = "1.7.2" # Dependency analysis -dependencyAnalysis = "3.10.0" +dependencyAnalysis = "3.11.0" # DI metro = "1.0.0" From 2f406eaf413bec0ea5e12b3534431871a4fe2f4a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:20:06 +0200 Subject: [PATCH 184/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18 (#6805) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18 * Fix breaking API changes --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- gradle/libs.versions.toml | 2 +- .../matrix/impl/fixtures/fakes/FakeFfiSpaceRoomList.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 831cff5052..a55a8b2adb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -179,7 +179,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.13" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.18" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSpaceRoomList.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSpaceRoomList.kt index c0ecc53d4f..56cc5b9a1f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSpaceRoomList.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiSpaceRoomList.kt @@ -40,7 +40,7 @@ class FakeFfiSpaceRoomList( return paginationStateResult() } - override fun rooms(): List { + override suspend fun rooms(): List { return roomsResult() } @@ -53,7 +53,7 @@ class FakeFfiSpaceRoomList( spaceRoomListPaginationStateListener?.onUpdate(state) } - override fun subscribeToRoomUpdate(listener: SpaceRoomListEntriesListener): TaskHandle { + override suspend fun subscribeToRoomUpdate(listener: SpaceRoomListEntriesListener): TaskHandle { spaceRoomListEntriesListener = listener return FakeFfiTaskHandle() } From f373c73e604392e2b9df68ae662462d60dbf7d28 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 14:43:49 +0200 Subject: [PATCH 185/471] Setting version for the release 26.05.2 --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index a3de4327b4..5e9a8694be 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 5 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 1 +private const val versionReleaseNumber = 2 object Versions { /** From d8793ca8f610e0247d39b987382a1e8ccd31d43e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 14:44:02 +0200 Subject: [PATCH 186/471] Adding fastlane file for version 26.05.2 --- fastlane/metadata/android/en-US/changelogs/202605020.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202605020.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202605020.txt b/fastlane/metadata/android/en-US/changelogs/202605020.txt new file mode 100644 index 0000000000..a4b397f1bb --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202605020.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 5af57906b51cc2a7423703ecb2e33be4a0cccdc3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 15:23:47 +0200 Subject: [PATCH 187/471] Changelog for version 26.05.2 --- CHANGES.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 19665d542c..9775c2fea5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,42 @@ +Changes in Element X v26.05.2 +============================= + + + +## What's Changed +### ✨ Features +* Remove SignInWithClassic FeatureFlag to enable the feature. by @bmarty in https://github.com/element-hq/element-x-android/pull/6698 +* Create a new room when inviting people in a DM by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6756 +* Remove LiveLocationSharing feature flag by @ganfra in https://github.com/element-hq/element-x-android/pull/6811 +### 🙌 Improvements +* Disable biometric unlock when we disable pin code unlock by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6781 +### 🐛 Bugfixes +* Fix room list duplicate-detection telemetry crashing before it can report by @jennaharris7 in https://github.com/element-hq/element-x-android/pull/6791 +* Only load full media on media viewer when it's the visible item by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6794 +* Attempt to fix room list item duplicates at midnight by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6793 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6798 +### 🧱 Build +* Fix Maestro again after changes to the invite flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6796 +* Renovate: Keep Guava on the Android variant and ignore jre-only upgrades by @bmarty in https://github.com/element-hq/element-x-android/pull/6776 +### Dependency upgrades +* Update dependency androidx.compose:compose-bom to v2026.05.00 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6784 +* Update dependency io.sentry:sentry-android to v8.41.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6787 +* Update kotlin by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6790 +* Update camera to v1.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6783 +* Update dependency androidx.webkit:webkit to v1.16.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6786 +* Update dependency com.google.firebase:firebase-bom to v34.13.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6789 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.18 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6805 +### Others +* Add MIDI playback by @cizra in https://github.com/element-hq/element-x-android/pull/6770 +* Show error message when using "Sign in with QR code" with a QR from a device that is also not signed in by @hughns in https://github.com/element-hq/element-x-android/pull/6802 + +## New Contributors +* @jennaharris7 made their first contribution in https://github.com/element-hq/element-x-android/pull/6791 +* @cizra made their first contribution in https://github.com/element-hq/element-x-android/pull/6770 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.05.1...v26.05.2 + Changes in Element X v26.05.1 ============================= From 882d5577a7d36649cf5747a0b867977109485d97 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 18:06:11 +0200 Subject: [PATCH 188/471] Add "Mark as read", "Mark as unread" in room settings. #6398 --- features/roomdetails/impl/build.gradle.kts | 1 + .../roomdetails/impl/RoomDetailsEvent.kt | 2 + .../roomdetails/impl/RoomDetailsPresenter.kt | 38 +++++++++++++++++ .../roomdetails/impl/RoomDetailsState.kt | 1 + .../impl/RoomDetailsStateProvider.kt | 4 +- .../roomdetails/impl/RoomDetailsView.kt | 41 +++++++++++++++++++ .../impl/src/main/res/values/localazy.xml | 2 + tools/localazy/config.json | 1 + 8 files changed, 89 insertions(+), 1 deletion(-) diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 2765a95a1e..23b23a3e62 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) + implementation(projects.libraries.push.api) implementation(projects.libraries.testtags) api(projects.features.roomdetails.api) api(projects.libraries.usersearch.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt index de801e8aae..a82766175d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt @@ -14,4 +14,6 @@ sealed interface RoomDetailsEvent { data object UnmuteNotification : RoomDetailsEvent data class CopyToClipboard(val text: String) : RoomDetailsEvent data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent + data object MarkAsRead : RoomDetailsEvent + data object MarkAsUnread : RoomDetailsEvent } 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 167cc2ced5..9f61c12d64 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 @@ -44,13 +44,17 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.canEditRolesAndPermissions import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomNotificationSettings +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.SessionPreferencesStore +import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -67,6 +71,8 @@ class RoomDetailsPresenter( private val analyticsService: AnalyticsService, private val clipboardHelper: ClipboardHelper, private val appPreferencesStore: AppPreferencesStore, + private val sessionPreferencesStore: SessionPreferencesStore, + private val notificationCleaner: NotificationCleaner, ) : Presenter { @Composable override fun present(): RoomDetailsState { @@ -79,6 +85,14 @@ class RoomDetailsPresenter( val roomTopic by remember { derivedStateOf { roomInfo.topic } } val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } } val joinRule by remember { derivedStateOf { roomInfo.joinRule } } + val hasNewContent by remember { + derivedStateOf { + roomInfo.numUnreadMessages > 0 || + roomInfo.numUnreadMentions > 0 || + roomInfo.numUnreadNotifications > 0 || + roomInfo.isMarkedUnread + } + } val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } } @@ -145,6 +159,8 @@ class RoomDetailsPresenter( clipboardHelper.copyPlainText(event.text) snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) } + is RoomDetailsEvent.MarkAsRead -> scope.markAsRead() + is RoomDetailsEvent.MarkAsUnread -> scope.markAsUnread() } } @@ -188,6 +204,7 @@ class RoomDetailsPresenter( showDebugInfo = isDeveloperModeEnabled, roomVersion = roomInfo.roomVersion, roomHistoryVisibility = roomInfo.historyVisibility, + hasNewContent = hasNewContent, eventSink = ::handleEvent, ) } @@ -241,4 +258,25 @@ class RoomDetailsPresenter( analyticsService.captureInteraction(Interaction.Name.MobileRoomFavouriteToggle) } } + + private fun CoroutineScope.markAsRead() = launch { + notificationCleaner.clearMessagesForRoom(client.sessionId, room.roomId) + room.setUnreadFlag(isUnread = false) + val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) { + ReceiptType.READ + } else { + ReceiptType.READ_PRIVATE + } + room.markAsRead(receiptType) + .onSuccess { + analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle) + } + } + + private fun CoroutineScope.markAsUnread() = launch { + room.setUnreadFlag(isUnread = true) + .onSuccess { + analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle) + } + } } 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 90a912e86f..bd3b90d416 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 @@ -52,6 +52,7 @@ data class RoomDetailsState( val showDebugInfo: Boolean, val roomVersion: String?, val roomHistoryVisibility: RoomHistoryVisibility, + val hasNewContent: Boolean, val eventSink: (RoomDetailsEvent) -> Unit ) { val roomBadges = buildList { 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 b569527d9c..7182d2ec04 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 @@ -34,7 +34,7 @@ open class RoomDetailsStateProvider : PreviewParameterProvider override val values: Sequence get() = sequenceOf( aRoomDetailsState(displayAdminSettings = true), - aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true), + aRoomDetailsState(roomTopic = RoomTopicState.Hidden, showDebugInfo = true, hasNewContent = true), aRoomDetailsState(roomTopic = RoomTopicState.CanAddTopic), aRoomDetailsState(isEncrypted = false), aRoomDetailsState(roomAlias = null), @@ -123,6 +123,7 @@ fun aRoomDetailsState( isTombstoned: Boolean = false, showDebugInfo: Boolean = false, roomHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Shared, + hasNewContent: Boolean = false, eventSink: (RoomDetailsEvent) -> Unit = {}, ) = RoomDetailsState( roomId = roomId, @@ -154,6 +155,7 @@ fun aRoomDetailsState( showDebugInfo = showDebugInfo, roomVersion = "12", roomHistoryVisibility = roomHistoryVisibility, + hasNewContent = hasNewContent, eventSink = eventSink, ) 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 cf5265b145..b30c73b9c8 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 @@ -8,6 +8,7 @@ package io.element.android.features.roomdetails.impl +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -188,6 +189,46 @@ fun RoomDetailsView( ) } + PreferenceCategory { + if (state.hasNewContent) { + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_roomlist_mark_as_read), + style = MaterialTheme.typography.bodyLarge, + ) + }, + onClick = { + state.eventSink(RoomDetailsEvent.MarkAsRead) + }, + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Vector(CompoundIcons.MarkAsRead()) + ), + trailingContent = ListItemContent.Custom { + Box( + modifier = modifier + .size(8.dp) + .clip(CircleShape) + .background(ElementTheme.colors.iconAccentPrimary) + ) + }, + ) + } else { + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.screen_roomlist_mark_as_unread), + ) + }, + onClick = { + state.eventSink(RoomDetailsEvent.MarkAsUnread) + }, + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Vector(CompoundIcons.MarkAsUnread()) + ), + ) + } + } PreferenceCategory { if (state.roomNotificationSettings != null) { NotificationItem( diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index d7092af70e..0287b5657b 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -132,6 +132,8 @@ "Roles" "Room details" "Roles & permissions" + "Mark as read" + "Mark as unread" "Add address" "Anyone in authorised spaces can join, but everyone else must request access." "Everyone must request access." diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 6ea495439e..cb9c2ecb0d 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -221,6 +221,7 @@ "screen_notification_settings_mentions_only_disclaimer", "screen_room_change_.*", "screen_room_roles_.*", + "screen_roomlist_mark_as_.*", "screen\\.edit_room_address\\..*", "screen\\.security_and_privacy\\..*" ] From 53fe12bdda6b64a754f6b8f6308be6fece865637 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 18:37:32 +0200 Subject: [PATCH 189/471] "Mark as unread" navigate back to the room list. #6398 --- .../element/android/appnav/LoggedInFlowNode.kt | 4 ++++ .../room/joined/JoinedRoomLoadedFlowNode.kt | 5 +++++ .../roomdetails/api/RoomDetailsEntryPoint.kt | 1 + .../roomdetails/impl/RoomDetailsFlowNode.kt | 4 ++++ .../roomdetails/impl/RoomDetailsNavigator.kt | 12 ++++++++++++ .../roomdetails/impl/RoomDetailsNode.kt | 10 ++++++++-- .../roomdetails/impl/RoomDetailsPresenter.kt | 17 +++++++++++++++-- 7 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNavigator.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 47e5b7c5b3..14230d7c5a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -382,6 +382,10 @@ class LoggedInFlowNode( } is NavTarget.Room -> { val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback { + override fun onDone() { + backstack.pop() + } + override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) { lifecycleScope.launch { attachRoom(roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = serverNames, clearBackstack = clearBackStack) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index a8e5921973..dbe53b75ac 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -82,6 +82,7 @@ class JoinedRoomLoadedFlowNode( plugins = plugins, ), DependencyInjectionGraphOwner { interface Callback : Plugin { + fun onDone() fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean = false) fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) fun navigateToGlobalNotificationSettings() @@ -142,6 +143,10 @@ class JoinedRoomLoadedFlowNode( private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { val callback = object : RoomDetailsEntryPoint.Callback { + override fun onDone() { + callback.onDone() + } + override fun navigateToGlobalNotificationSettings() { callback.navigateToGlobalNotificationSettings() } diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index bbaac0cbc1..3474711b55 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -38,6 +38,7 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint { data class Params(val initialElement: InitialTarget) : NodeInputs interface Callback : Plugin { + fun onDone() fun navigateToGlobalNotificationSettings() fun navigateToDeveloperSettings() fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean = false) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 4cf5056b4e..d4108a77e3 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -176,6 +176,10 @@ class RoomDetailsFlowNode( return when (navTarget) { NavTarget.RoomDetails -> { val roomDetailsCallback = object : RoomDetailsNode.Callback { + override fun navigateBack() { + callback.onDone() + } + override fun navigateToRoomMemberList() { backstack.push(NavTarget.RoomMemberList) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNavigator.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNavigator.kt new file mode 100644 index 0000000000..9e2b77a260 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNavigator.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.roomdetails.impl + +interface RoomDetailsNavigator { + fun onDone() +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index e9aad5b1d2..7f22c7e111 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -42,12 +42,13 @@ import io.element.android.libraries.androidutils.R as AndroidUtilsR class RoomDetailsNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, - private val presenter: RoomDetailsPresenter, + presenterFactory: RoomDetailsPresenter.Factory, private val room: BaseRoom, private val analyticsService: AnalyticsService, private val leaveRoomRenderer: LeaveRoomRenderer, -) : Node(buildContext, plugins = plugins) { +) : Node(buildContext, plugins = plugins), RoomDetailsNavigator { interface Callback : Plugin { + fun navigateBack() fun navigateToRoomMemberList() fun navigateToInviteMembers() fun navigateToRoomDetailsEdit() @@ -65,6 +66,7 @@ class RoomDetailsNode( fun navigateToSelectNewOwnersWhenLeaving() } + private val presenter = presenterFactory.create(this) private val callback: Callback = callback() init { @@ -144,4 +146,8 @@ class RoomDetailsNode( } ) } + + override fun onDone() { + callback.navigateBack() + } } 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 9f61c12d64..b27fbe2f50 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 @@ -17,7 +17,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.features.knockrequests.api.KnockRequestPermissions import io.element.android.features.knockrequests.api.knockRequestPermissions @@ -59,8 +61,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -@Inject +@AssistedInject class RoomDetailsPresenter( + @Assisted private val navigator: RoomDetailsNavigator, private val client: MatrixClient, private val room: JoinedRoom, private val notificationSettingsService: NotificationSettingsService, @@ -74,6 +77,13 @@ class RoomDetailsPresenter( private val sessionPreferencesStore: SessionPreferencesStore, private val notificationCleaner: NotificationCleaner, ) : Presenter { + @AssistedFactory + interface Factory { + fun create( + navigator: RoomDetailsNavigator, + ): RoomDetailsPresenter + } + @Composable override fun present(): RoomDetailsState { val scope = rememberCoroutineScope() @@ -278,5 +288,8 @@ class RoomDetailsPresenter( .onSuccess { analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle) } + .onSuccess { + navigator.onDone() + } } } From 2248cd20f3984bda53cbcdaaf93966c60b0cc416 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 16:39:00 +0000 Subject: [PATCH 190/471] Update dependency net.zetetic:sqlcipher-android to v4.16.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a55a8b2adb..25aeecd174 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -201,7 +201,7 @@ matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } -sqlcipher = "net.zetetic:sqlcipher-android:4.15.0" +sqlcipher = "net.zetetic:sqlcipher-android:4.16.0" sqlite = "androidx.sqlite:sqlite-ktx:2.6.2" unifiedpush = "org.unifiedpush.android:connector:3.3.2" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" From c59b96de667b114181de316acdd6d7c2f534fde1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 09:00:11 +0200 Subject: [PATCH 191/471] [Link new device] Automatically rotate the QR Code --- .../impl/LinkNewDeviceFlowNode.kt | 10 +++- .../impl/LinkNewMobileHandler.kt | 4 ++ .../impl/screens/qrcode/ShowQrCodeNode.kt | 7 ++- .../screens/qrcode/ShowQrCodePresenter.kt | 58 +++++++++++++++++++ .../impl/screens/qrcode/ShowQrCodeState.kt | 14 +++++ .../screens/qrcode/ShowQrCodeStateProvider.kt | 27 +++++++++ .../impl/screens/qrcode/ShowQrCodeView.kt | 34 ++++++++--- .../api/linknewdevice/LinkMobileHandler.kt | 1 + .../linknewdevice/RustLinkMobileHandler.kt | 9 ++- 9 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt create mode 100644 features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt create mode 100644 features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt index e90d318267..61645ead9d 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewDeviceFlowNode.kt @@ -144,8 +144,14 @@ class LinkNewDeviceFlowNode( navigateToError(linkMobileStep.errorType) } is LinkMobileStep.QrReady -> { - // The QrCode is ready, navigate to its display - backstack.push(NavTarget.MobileShowQrCode(linkMobileStep.data)) + // The QrCode is ready, navigate to its display, if not already there + val navTarget = backstack.elements.value.last().key.navTarget + if (navTarget !is NavTarget.MobileShowQrCode) { + backstack.push(NavTarget.MobileShowQrCode(linkMobileStep.data)) + } + } + LinkMobileStep.QrRotating -> { + // This step is handled in ShowQrCodePresenter } is LinkMobileStep.QrScanned -> { backstack.replace(NavTarget.MobileEnterNumber) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt index 157d946eaa..12cf3af3b9 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt @@ -65,4 +65,8 @@ class LinkNewMobileHandler( linkMobileStepFlow.emit(LinkMobileStep.Uninitialized) } } + + fun rotateQrCode() { + createAndStartNewHandler() + } } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt index a884c3e97f..20bd50f488 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeNode.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.di.SessionScope class ShowQrCodeNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, + showQrCodePresenterFactory: ShowQrCodePresenter.Factory, ) : Node(buildContext, plugins = plugins) { class Inputs( val data: String, @@ -36,11 +37,15 @@ class ShowQrCodeNode( private val inputs: Inputs = inputs() private val callback: Callback = callback() + private val showQrCodePresenter: ShowQrCodePresenter = showQrCodePresenterFactory.create( + initialData = inputs.data, + ) @Composable override fun View(modifier: Modifier) { + val state = showQrCodePresenter.present() ShowQrCodeView( - data = inputs.data, + state = state, modifier = modifier, onBackClick = callback::navigateBack, ) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt new file mode 100644 index 0000000000..1688a6469d --- /dev/null +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.linknewdevice.impl.screens.qrcode + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject +import io.element.android.features.linknewdevice.impl.LinkNewMobileHandler +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep +import io.element.android.libraries.matrix.api.logs.LoggerTags +import timber.log.Timber + +private val tag = LoggerTag("ShowQrCodePresenter", LoggerTags.linkNewDevice) + +@AssistedInject +class ShowQrCodePresenter( + @Assisted private val initialData: String, + private val linkNewMobileHandler: LinkNewMobileHandler, +) : Presenter { + @AssistedFactory + interface Factory { + fun create(initialData: String): ShowQrCodePresenter + } + + @Composable + override fun present(): ShowQrCodeState { + val data by produceState>(AsyncData.Success(initialData)) { + linkNewMobileHandler.stepFlow.collect { step -> + when (step) { + is LinkMobileStep.QrReady -> { + value = AsyncData.Success(step.data) + } + is LinkMobileStep.QrRotating -> { + Timber.tag(tag.value).d("Rotating QrCode") + linkNewMobileHandler.rotateQrCode() + value = AsyncData.Loading() + } + else -> Unit + } + } + } + + return ShowQrCodeState( + data = data, + ) + } +} diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt new file mode 100644 index 0000000000..e69dde8264 --- /dev/null +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.linknewdevice.impl.screens.qrcode + +import io.element.android.libraries.architecture.AsyncData + +data class ShowQrCodeState( + val data: AsyncData, +) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt new file mode 100644 index 0000000000..0c987bc5e7 --- /dev/null +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.linknewdevice.impl.screens.qrcode + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncData + +class ShowQrCodeStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aShowQrCodeState(), + ShowQrCodeState( + data = AsyncData.Loading(), + ), + ) +} + +private fun aShowQrCodeState( + data: AsyncData.Success = AsyncData.Success("DATA"), +) = ShowQrCodeState( + data = data, +) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt index 501415f621..cc55ed374d 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt @@ -9,6 +9,7 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -21,6 +22,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.linknewdevice.impl.R @@ -30,6 +32,7 @@ import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.LocalBuildMeta +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.utils.annotatedTextWithBold import io.element.android.libraries.qrcode.QrCodeImage import kotlinx.collections.immutable.persistentListOf @@ -40,7 +43,7 @@ import kotlinx.collections.immutable.persistentListOf */ @Composable fun ShowQrCodeView( - data: String, + state: ShowQrCodeState, onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -55,11 +58,24 @@ fun ShowQrCodeView( Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, ) { - QrCodeImage( - data = data, - modifier = Modifier - .size(220.dp) - ) + when (val str = state.data.dataOrNull()) { + null -> { + Box( + modifier = Modifier + .size(220.dp), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() + } + } + else -> { + QrCodeImage( + data = str, + modifier = Modifier + .size(220.dp) + ) + } + } Spacer(modifier = Modifier.height(32.dp)) NumberedListOrganism( modifier = Modifier.fillMaxSize(), @@ -81,9 +97,11 @@ fun ShowQrCodeView( @PreviewsDayNight @Composable -internal fun ShowQrCodeViewPreview() = ElementPreview { +internal fun ShowQrCodeViewPreview( + @PreviewParameter(ShowQrCodeStateProvider::class) state: ShowQrCodeState, +) = ElementPreview { ShowQrCodeView( - data = "DATA", + state = state, onBackClick = { }, ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt index 0c261cdd1a..1947729c7f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/linknewdevice/LinkMobileHandler.kt @@ -18,6 +18,7 @@ sealed interface LinkMobileStep { data object Uninitialized : LinkMobileStep data object Starting : LinkMobileStep data class QrReady(val data: String) : LinkMobileStep + data object QrRotating : LinkMobileStep data class WaitingForAuth(val verificationUri: String) : LinkMobileStep data class QrScanned(val checkCodeSender: CheckCodeSender) : LinkMobileStep data class Error(val errorType: ErrorType) : LinkMobileStep diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt index 6d212d4784..77ae28acea 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt @@ -53,7 +53,14 @@ class RustLinkMobileHandler( _linkMobileStep.emit(LinkMobileStep.Done) } catch (e: HumanQrGrantLoginException) { Timber.tag(tag.value).w(e, "Error during QR login grant") - _linkMobileStep.emit(LinkMobileStep.Error(e.map())) + // Catch timeout here? + if (_linkMobileStep.value is LinkMobileStep.QrReady + && e is HumanQrGrantLoginException.NotFound) { + Timber.tag(tag.value).d("Emit QrRotating due to HumanQrGrantLoginException.NotFound") + _linkMobileStep.emit(LinkMobileStep.QrRotating) + } else { + _linkMobileStep.emit(LinkMobileStep.Error(e.map())) + } } } From a6f7c05458c7a0777e6c8c695f719188babc952c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 09:24:21 +0200 Subject: [PATCH 192/471] [Link new device] Automatically rotate the QR Code - limit number of rotation to 10. --- .../impl/LinkNewMobileHandler.kt | 8 ++++++++ .../screens/qrcode/ShowQrCodePresenter.kt | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt index 12cf3af3b9..18d67f577a 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/LinkNewMobileHandler.kt @@ -12,6 +12,7 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.linknewdevice.ErrorType import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep import io.element.android.libraries.matrix.api.logs.LoggerTags @@ -69,4 +70,11 @@ class LinkNewMobileHandler( fun rotateQrCode() { createAndStartNewHandler() } + + fun onTooManyRotation() { + reset() + sessionScope.launch { + linkMobileStepFlow.emit(LinkMobileStep.Error(ErrorType.Expired("Too many QR code rotations"))) + } + } } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt index 1688a6469d..3deff8c603 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -9,7 +9,10 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject @@ -35,6 +38,7 @@ class ShowQrCodePresenter( @Composable override fun present(): ShowQrCodeState { + var qrCodeRotationCounter by remember { mutableIntStateOf(MAX_QR_CODE_ROTATION) } val data by produceState>(AsyncData.Success(initialData)) { linkNewMobileHandler.stepFlow.collect { step -> when (step) { @@ -42,9 +46,14 @@ class ShowQrCodePresenter( value = AsyncData.Success(step.data) } is LinkMobileStep.QrRotating -> { - Timber.tag(tag.value).d("Rotating QrCode") - linkNewMobileHandler.rotateQrCode() - value = AsyncData.Loading() + if (qrCodeRotationCounter-- > 0) { + Timber.tag(tag.value).d("Rotating QrCode") + linkNewMobileHandler.rotateQrCode() + value = AsyncData.Loading() + } else { + Timber.tag(tag.value).w("Max QR code rotation reached, not rotating anymore") + linkNewMobileHandler.onTooManyRotation() + } } else -> Unit } @@ -55,4 +64,8 @@ class ShowQrCodePresenter( data = data, ) } + + companion object { + const val MAX_QR_CODE_ROTATION = 10 + } } From 18bc6a27c48622dfcbdac8fd077f3fb90f4e5af1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 10:43:47 +0200 Subject: [PATCH 193/471] [Link new device] Improve UI transition between QRCodes. --- .../screens/qrcode/ShowQrCodePresenter.kt | 47 +++++++++++++-- .../impl/screens/qrcode/ShowQrCodeState.kt | 4 +- .../screens/qrcode/ShowQrCodeStateProvider.kt | 17 ++++-- .../impl/screens/qrcode/ShowQrCodeView.kt | 59 +++++++++++++------ 4 files changed, 99 insertions(+), 28 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt index 3deff8c603..6a7e7cfcf5 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -22,6 +22,9 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep import io.element.android.libraries.matrix.api.logs.LoggerTags +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import timber.log.Timber private val tag = LoggerTag("ShowQrCodePresenter", LoggerTags.linkNewDevice) @@ -36,20 +39,54 @@ class ShowQrCodePresenter( fun create(initialData: String): ShowQrCodePresenter } + private var loadingJob: Job? = null + @Composable override fun present(): ShowQrCodeState { var qrCodeRotationCounter by remember { mutableIntStateOf(MAX_QR_CODE_ROTATION) } - val data by produceState>(AsyncData.Success(initialData)) { + val state by produceState( + initialValue = ShowQrCodeState( + data1 = AsyncData.Success(initialData), + data2 = AsyncData.Uninitialized, + dataToRender = 1, + ) + ) { linkNewMobileHandler.stepFlow.collect { step -> + val currentValue = value when (step) { is LinkMobileStep.QrReady -> { - value = AsyncData.Success(step.data) + loadingJob?.cancel() + if (currentValue.dataToRender == 1) { + value = currentValue.copy( + data2 = AsyncData.Success(step.data), + dataToRender = 2, + ) + } else { + value = currentValue.copy( + data1 = AsyncData.Success(step.data), + dataToRender = 1, + ) + } } is LinkMobileStep.QrRotating -> { if (qrCodeRotationCounter-- > 0) { Timber.tag(tag.value).d("Rotating QrCode") linkNewMobileHandler.rotateQrCode() - value = AsyncData.Loading() + // Ensure that outdated data is not rendered too long while rotating QR code + loadingJob = launch { + delay(1000) + if (currentValue.dataToRender == 1) { + value = currentValue.copy( + data2 = AsyncData.Loading(), + dataToRender = 2, + ) + } else { + value = currentValue.copy( + data1 = AsyncData.Loading(), + dataToRender = 1, + ) + } + } } else { Timber.tag(tag.value).w("Max QR code rotation reached, not rotating anymore") linkNewMobileHandler.onTooManyRotation() @@ -60,9 +97,7 @@ class ShowQrCodePresenter( } } - return ShowQrCodeState( - data = data, - ) + return state } companion object { diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt index e69dde8264..76302b71aa 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt @@ -10,5 +10,7 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode import io.element.android.libraries.architecture.AsyncData data class ShowQrCodeState( - val data: AsyncData, + val data1: AsyncData, + val data2: AsyncData, + val dataToRender: Int, ) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt index 0c987bc5e7..dc7c12b847 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt @@ -14,14 +14,23 @@ class ShowQrCodeStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aShowQrCodeState(), - ShowQrCodeState( - data = AsyncData.Loading(), + aShowQrCodeState( + data1 = AsyncData.Loading(), + ), + aShowQrCodeState( + data1 = AsyncData.Success("DATA"), + data2 = AsyncData.Success("DATA2"), + dataToRender = 2, ), ) } private fun aShowQrCodeState( - data: AsyncData.Success = AsyncData.Success("DATA"), + data1: AsyncData = AsyncData.Success("DATA"), + data2: AsyncData = AsyncData.Uninitialized, + dataToRender: Int = 1, ) = ShowQrCodeState( - data = data, + data1 = data1, + data2 = data2, + dataToRender = dataToRender, ) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt index cc55ed374d..0791847288 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt @@ -9,6 +9,9 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -58,23 +61,15 @@ fun ShowQrCodeView( Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, ) { - when (val str = state.data.dataOrNull()) { - null -> { - Box( - modifier = Modifier - .size(220.dp), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } - } - else -> { - QrCodeImage( - data = str, - modifier = Modifier - .size(220.dp) - ) - } + Box { + QrCodeOrLoading( + isVisible = state.dataToRender == 1, + data = state.data1.dataOrNull(), + ) + QrCodeOrLoading( + isVisible = state.dataToRender == 2, + data = state.data2.dataOrNull(), + ) } Spacer(modifier = Modifier.height(32.dp)) NumberedListOrganism( @@ -95,6 +90,36 @@ fun ShowQrCodeView( } } +@Composable +private fun QrCodeOrLoading( + isVisible: Boolean, + data: String?, + modifier: Modifier = Modifier, +) { + AnimatedVisibility( + modifier = modifier, + visible = isVisible, + enter = fadeIn(), + exit = fadeOut(), + ) { + if (data == null) { + Box( + modifier = Modifier + .size(220.dp), + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() + } + } else { + QrCodeImage( + data = data, + modifier = Modifier + .size(220.dp) + ) + } + } +} + @PreviewsDayNight @Composable internal fun ShowQrCodeViewPreview( From 75feaa7ee7ebb1291f81d68e53d7ffec93017b01 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 10:59:39 +0200 Subject: [PATCH 194/471] [Link new device] Fix and add tests. --- .../screens/qrcode/ShowQrCodeStateProvider.kt | 2 +- .../screens/qrcode/ShowQrCodePresenterTest.kt | 105 ++++++++++++++++++ .../impl/screens/qrcode/ShowQrCodeViewTest.kt | 2 +- 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt index dc7c12b847..6079d0918c 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt @@ -25,7 +25,7 @@ class ShowQrCodeStateProvider : PreviewParameterProvider { ) } -private fun aShowQrCodeState( +internal fun aShowQrCodeState( data1: AsyncData = AsyncData.Success("DATA"), data2: AsyncData = AsyncData.Uninitialized, dataToRender: Int = 1, diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt new file mode 100644 index 0000000000..8ecc0ea008 --- /dev/null +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.linknewdevice.impl.screens.qrcode + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.linknewdevice.impl.LinkNewMobileHandler +import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler +import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileStep +import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.linknewdevice.FakeLinkMobileHandler +import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class ShowQrCodePresenterTest { + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - initial state`() = runTest { + createPresenter().test { + val initialState = awaitItem() + assertThat(initialState.data1.dataOrNull()).isEqualTo("DATA") + assertThat(initialState.data2.isUninitialized()).isTrue() + assertThat(initialState.dataToRender).isEqualTo(1) + } + } + + @Test + fun `present - when handler emits QrRotating, the presenter requests to rotate the QrCode`() = runTest { + val linkMobileHandler = FakeLinkMobileHandler( + startResult = {}, + ) + val createLinkMobileHandlerResult = lambdaRecorder> { + Result.success(linkMobileHandler) + } + val matrixClient = FakeMatrixClient( + sessionCoroutineScope = backgroundScope, + createLinkMobileHandlerResult = createLinkMobileHandlerResult, + ) + val linkNewMobileHandler = LinkNewMobileHandler(matrixClient) + linkNewMobileHandler.createAndStartNewHandler() + createPresenter( + linkNewMobileHandler = linkNewMobileHandler, + ).test { + awaitItem() + linkMobileHandler.emitStep( + LinkMobileStep.QrRotating + ) + runCurrent() + val finalState = awaitItem() + assertThat(finalState.data2.isLoading()).isTrue() + assertThat(finalState.dataToRender).isEqualTo(2) + createLinkMobileHandlerResult.assertions().isCalledExactly(2) + } + } + + @Test + fun `present - when handler emits QrRotating, the presenter requests to rotate the QrCode and the code is rotated`() = runTest { + val linkMobileHandler = FakeLinkMobileHandler( + startResult = {}, + ) + val matrixClient = FakeMatrixClient( + sessionCoroutineScope = backgroundScope, + createLinkMobileHandlerResult = { Result.success(linkMobileHandler) }, + ) + val linkNewMobileHandler = LinkNewMobileHandler(matrixClient) + linkNewMobileHandler.createAndStartNewHandler() + createPresenter( + linkNewMobileHandler = linkNewMobileHandler, + ).test { + awaitItem() + linkMobileHandler.emitStep( + LinkMobileStep.QrRotating + ) + runCurrent() + linkMobileHandler.emitStep( + LinkMobileStep.QrReady("DATA2") + ) + val finalState = awaitItem() + assertThat(finalState.data1.dataOrNull()).isEqualTo("DATA") + assertThat(finalState.data2.dataOrNull()).isEqualTo("DATA2") + assertThat(finalState.dataToRender).isEqualTo(2) + } + } + + private fun createPresenter( + linkNewMobileHandler: LinkNewMobileHandler = LinkNewMobileHandler(FakeMatrixClient()), + ) = ShowQrCodePresenter( + initialData = "DATA", + linkNewMobileHandler = linkNewMobileHandler, + ) +} diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt index d552c2bff6..7927eeed77 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeViewTest.kt @@ -37,7 +37,7 @@ class ShowQrCodeViewTest { ) { setContent { ShowQrCodeView( - data = "DATA", + state = aShowQrCodeState(), onBackClick = onBackClick, ) } From 33c7c7555035392571c8902df75de7bf3ad732bc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 14:24:02 +0200 Subject: [PATCH 195/471] Fix detekt issue --- .../matrix/impl/linknewdevice/RustLinkMobileHandler.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt index 77ae28acea..cb387a9d21 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt @@ -51,16 +51,18 @@ class RustLinkMobileHandler( ) // We emit Done in case the progress listener was deallocated before generate() sent the Done _linkMobileStep.emit(LinkMobileStep.Done) - } catch (e: HumanQrGrantLoginException) { + } catch (e: HumanQrGrantLoginException.NotFound) { Timber.tag(tag.value).w(e, "Error during QR login grant") // Catch timeout here? - if (_linkMobileStep.value is LinkMobileStep.QrReady - && e is HumanQrGrantLoginException.NotFound) { + if (_linkMobileStep.value is LinkMobileStep.QrReady) { Timber.tag(tag.value).d("Emit QrRotating due to HumanQrGrantLoginException.NotFound") _linkMobileStep.emit(LinkMobileStep.QrRotating) } else { _linkMobileStep.emit(LinkMobileStep.Error(e.map())) } + } catch (e: HumanQrGrantLoginException) { + Timber.tag(tag.value).w(e, "Error during QR login grant") + _linkMobileStep.emit(LinkMobileStep.Error(e.map())) } } From 1473f5e806447ca96d62b9761a42654a4abb6497 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 14:41:47 +0200 Subject: [PATCH 196/471] [Link new device] and add tests. --- .../RustLinkMobileHandlerTest.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt index 9f71cb7169..ad751cc86b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt @@ -118,6 +118,35 @@ class RustLinkMobileHandlerTest { } } + @Test + fun `when start throws HumanQrGrantLoginException_NotFound when in state QrReady, the handler emits QrRotating step`() = runTest { + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + generateResult = { + completable.await() + throw HumanQrGrantLoginException.NotFound("Timeout") + } + ) + val sut = createRustLinkMobileHandler( + handler, + ) + sut.linkMobileStep.test { + val initialItem = awaitItem() + assertThat(initialItem).isEqualTo(LinkMobileStep.Uninitialized) + backgroundScope.launch { + sut.start() + } + runCurrent() + handler.emitGenerateProgress(GrantGeneratedQrLoginProgress.QrReady(FakeFfiQrCodeData(toBytesResult = { QR_CODE_DATA_RECIPROCATE }))) + val readyState = awaitItem() + assertThat(readyState).isInstanceOf(LinkMobileStep.QrReady::class.java) + // generate returns, error is emitted + completable.complete(Unit) + val qrRotatingState = awaitItem() + assertThat(qrRotatingState).isEqualTo(LinkMobileStep.QrRotating) + } + } + private fun TestScope.createRustLinkMobileHandler( handler: FakeFfiGrantLoginWithQrCodeHandler = FakeFfiGrantLoginWithQrCodeHandler(), ) = RustLinkMobileHandler( From 900b888044371a0f2bd53ab6a5aa4f08b1135ab7 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 19 May 2026 13:27:57 +0000 Subject: [PATCH 197/471] Update screenshots --- ...nknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en.png | 3 +++ ...nknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png | 3 +++ ...newdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en.png | 3 +++ ...newdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en.png new file mode 100644 index 0000000000..d2ab2dcb1e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48cafd6b98791b64e4cc6a16c602f17e3a886659698c3c93bc4acaf875337e0f +size 31102 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png new file mode 100644 index 0000000000..2f06e29863 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:151eaa4b5619afd76b94de518dc2868d735b6b7a167056941e1f429520c3bf0d +size 31836 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en.png new file mode 100644 index 0000000000..c4593656d1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f6f8d1282ae47a5240aec2b2c63a137b8e3e25ec8b746cf2d82d0fa7e0c0a34 +size 30287 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png new file mode 100644 index 0000000000..99f134760d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa2303b621070608ba79673322f61bfa686b9729eb445913dd8d059d62c9764a +size 32374 From 5dd897dae81adfdf55601c7b16541180b89c9106 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 19 May 2026 19:00:58 +0200 Subject: [PATCH 198/471] Use AnimatedContent instead of reinventing the wheel. --- .../screens/qrcode/ShowQrCodePresenter.kt | 33 +++--------- .../impl/screens/qrcode/ShowQrCodeState.kt | 4 +- .../screens/qrcode/ShowQrCodeStateProvider.kt | 15 ++---- .../impl/screens/qrcode/ShowQrCodeView.kt | 52 ++++++++----------- .../screens/qrcode/ShowQrCodePresenterTest.kt | 11 ++-- ...screens.qrcode_ShowQrCodeView_Day_2_en.png | 3 -- ...reens.qrcode_ShowQrCodeView_Night_2_en.png | 3 -- 7 files changed, 37 insertions(+), 84 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt index 6a7e7cfcf5..21071a6831 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenter.kt @@ -46,27 +46,16 @@ class ShowQrCodePresenter( var qrCodeRotationCounter by remember { mutableIntStateOf(MAX_QR_CODE_ROTATION) } val state by produceState( initialValue = ShowQrCodeState( - data1 = AsyncData.Success(initialData), - data2 = AsyncData.Uninitialized, - dataToRender = 1, + data = AsyncData.Success(initialData), ) ) { linkNewMobileHandler.stepFlow.collect { step -> - val currentValue = value when (step) { is LinkMobileStep.QrReady -> { loadingJob?.cancel() - if (currentValue.dataToRender == 1) { - value = currentValue.copy( - data2 = AsyncData.Success(step.data), - dataToRender = 2, - ) - } else { - value = currentValue.copy( - data1 = AsyncData.Success(step.data), - dataToRender = 1, - ) - } + value = ShowQrCodeState( + data = AsyncData.Success(step.data), + ) } is LinkMobileStep.QrRotating -> { if (qrCodeRotationCounter-- > 0) { @@ -75,17 +64,9 @@ class ShowQrCodePresenter( // Ensure that outdated data is not rendered too long while rotating QR code loadingJob = launch { delay(1000) - if (currentValue.dataToRender == 1) { - value = currentValue.copy( - data2 = AsyncData.Loading(), - dataToRender = 2, - ) - } else { - value = currentValue.copy( - data1 = AsyncData.Loading(), - dataToRender = 1, - ) - } + value = ShowQrCodeState( + data = AsyncData.Loading(), + ) } } else { Timber.tag(tag.value).w("Max QR code rotation reached, not rotating anymore") diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt index 76302b71aa..e69dde8264 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeState.kt @@ -10,7 +10,5 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode import io.element.android.libraries.architecture.AsyncData data class ShowQrCodeState( - val data1: AsyncData, - val data2: AsyncData, - val dataToRender: Int, + val data: AsyncData, ) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt index 6079d0918c..e6d33c2544 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeStateProvider.kt @@ -15,22 +15,13 @@ class ShowQrCodeStateProvider : PreviewParameterProvider { get() = sequenceOf( aShowQrCodeState(), aShowQrCodeState( - data1 = AsyncData.Loading(), - ), - aShowQrCodeState( - data1 = AsyncData.Success("DATA"), - data2 = AsyncData.Success("DATA2"), - dataToRender = 2, + data = AsyncData.Loading(), ), ) } internal fun aShowQrCodeState( - data1: AsyncData = AsyncData.Success("DATA"), - data2: AsyncData = AsyncData.Uninitialized, - dataToRender: Int = 1, + data: AsyncData = AsyncData.Success("DATA"), ) = ShowQrCodeState( - data1 = data1, - data2 = data2, - dataToRender = dataToRender, + data = data, ) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt index 0791847288..ee38342ce2 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt @@ -9,9 +9,11 @@ package io.element.android.features.linknewdevice.impl.screens.qrcode -import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -44,6 +46,7 @@ import kotlinx.collections.immutable.persistentListOf * QrCode display screen: * https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=2027-23617 */ +@OptIn(ExperimentalAnimationApi::class) @Composable fun ShowQrCodeView( state: ShowQrCodeState, @@ -61,14 +64,15 @@ fun ShowQrCodeView( Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, ) { - Box { + AnimatedContent( + targetState = state.data.dataOrNull(), + transitionSpec = { + fadeIn().togetherWith(fadeOut()) + } + ) { data -> QrCodeOrLoading( - isVisible = state.dataToRender == 1, - data = state.data1.dataOrNull(), - ) - QrCodeOrLoading( - isVisible = state.dataToRender == 2, - data = state.data2.dataOrNull(), + modifier = modifier.size(220.dp), + data = data, ) } Spacer(modifier = Modifier.height(32.dp)) @@ -92,31 +96,21 @@ fun ShowQrCodeView( @Composable private fun QrCodeOrLoading( - isVisible: Boolean, data: String?, modifier: Modifier = Modifier, ) { - AnimatedVisibility( - modifier = modifier, - visible = isVisible, - enter = fadeIn(), - exit = fadeOut(), - ) { - if (data == null) { - Box( - modifier = Modifier - .size(220.dp), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } - } else { - QrCodeImage( - data = data, - modifier = Modifier - .size(220.dp) - ) + if (data == null) { + Box( + modifier = modifier, + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() } + } else { + QrCodeImage( + modifier = modifier, + data = data, + ) } } diff --git a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt index 8ecc0ea008..f92cf66102 100644 --- a/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt +++ b/features/linknewdevice/impl/src/test/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodePresenterTest.kt @@ -32,9 +32,7 @@ class ShowQrCodePresenterTest { fun `present - initial state`() = runTest { createPresenter().test { val initialState = awaitItem() - assertThat(initialState.data1.dataOrNull()).isEqualTo("DATA") - assertThat(initialState.data2.isUninitialized()).isTrue() - assertThat(initialState.dataToRender).isEqualTo(1) + assertThat(initialState.data.dataOrNull()).isEqualTo("DATA") } } @@ -61,8 +59,7 @@ class ShowQrCodePresenterTest { ) runCurrent() val finalState = awaitItem() - assertThat(finalState.data2.isLoading()).isTrue() - assertThat(finalState.dataToRender).isEqualTo(2) + assertThat(finalState.data.isLoading()).isTrue() createLinkMobileHandlerResult.assertions().isCalledExactly(2) } } @@ -90,9 +87,7 @@ class ShowQrCodePresenterTest { LinkMobileStep.QrReady("DATA2") ) val finalState = awaitItem() - assertThat(finalState.data1.dataOrNull()).isEqualTo("DATA") - assertThat(finalState.data2.dataOrNull()).isEqualTo("DATA2") - assertThat(finalState.dataToRender).isEqualTo(2) + assertThat(finalState.data.dataOrNull()).isEqualTo("DATA2") } } diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png deleted file mode 100644 index 2f06e29863..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:151eaa4b5619afd76b94de518dc2868d735b6b7a167056941e1f429520c3bf0d -size 31836 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png deleted file mode 100644 index 99f134760d..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_2_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa2303b621070608ba79673322f61bfa686b9729eb445913dd8d059d62c9764a -size 32374 From dc05388ccaf8bd662583c9c8e6fd82b60d0407f8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 10:53:50 +0200 Subject: [PATCH 199/471] Update tests. --- .../FakeJoinedRoomLoadedFlowNodeCallback.kt | 1 + features/roomdetails/impl/build.gradle.kts | 1 + .../roomdetails/impl/RoomDetailsPresenter.kt | 2 - .../impl/DefaultRoomDetailsEntryPointTest.kt | 1 + .../impl/FakeRoomDetailsNavigator.kt | 16 ++++ .../roomdetails/impl/MatrixRoomFixture.kt | 9 +- .../impl/RoomDetailsPresenterTest.kt | 94 ++++++++++++++++++- 7 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/FakeRoomDetailsNavigator.kt diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt index b0669148dd..ec36f3d32d 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.tests.testutils.lambda.lambdaError class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback { + override fun onDone() = lambdaError() override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) = lambdaError() override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() override fun navigateToGlobalNotificationSettings() = lambdaError() diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 23b23a3e62..77e2d0c229 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) + testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.call.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 b27fbe2f50..e6617d1dd5 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 @@ -287,8 +287,6 @@ class RoomDetailsPresenter( room.setUnreadFlag(isUnread = true) .onSuccess { analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle) - } - .onSuccess { navigator.onDone() } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index 597cc82f7d..9a97380d21 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -68,6 +68,7 @@ class DefaultRoomDetailsEntryPointTest { ) } val callback = object : RoomDetailsEntryPoint.Callback { + override fun onDone() = lambdaError() override fun navigateToGlobalNotificationSettings() = lambdaError() override fun navigateToDeveloperSettings() = lambdaError() override fun navigateToRoom(roomId: RoomId, serverNames: List, clearBackStack: Boolean) = lambdaError() diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/FakeRoomDetailsNavigator.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/FakeRoomDetailsNavigator.kt new file mode 100644 index 0000000000..b416902dab --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/FakeRoomDetailsNavigator.kt @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.roomdetails.impl + +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeRoomDetailsNavigator( + private val onDoneResult: () -> Unit = { lambdaError() } +) : RoomDetailsNavigator { + override fun onDone() = onDoneResult() +} diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt index 2d85744345..edb9115a7e 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt @@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_ROOM_ALIAS import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -28,7 +29,7 @@ import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.tests.testutils.lambda.lambdaError -fun aRoom( +fun aFakeBaseRoom( sessionId: SessionId = A_SESSION_ID, roomId: RoomId = A_ROOM_ID, displayName: String = A_ROOM_NAME, @@ -49,6 +50,7 @@ fun aRoom( getUpdatedMemberResult: (UserId) -> Result = { lambdaError() }, userRoleResult: () -> Result = { lambdaError() }, setIsFavoriteResult: (Boolean) -> Result = { lambdaError() }, + markAsReadResult: (ReceiptType) -> Result = { lambdaError() }, ) = FakeBaseRoom( sessionId = sessionId, roomId = roomId, @@ -57,6 +59,7 @@ fun aRoom( getUpdatedMemberResult = getUpdatedMemberResult, userRoleResult = userRoleResult, setIsFavoriteResult = setIsFavoriteResult, + markAsReadResult = markAsReadResult, roomPermissions = roomPermissions, initialRoomInfo = aRoomInfo( name = displayName, @@ -106,6 +109,7 @@ fun aJoinedRoom( publishRoomAliasInRoomDirectoryResult: (RoomAlias) -> Result = { lambdaError() }, removeRoomAliasFromRoomDirectoryResult: (RoomAlias) -> Result = { lambdaError() }, setIsFavoriteResult: (Boolean) -> Result = { lambdaError() }, + markAsReadResult: (ReceiptType) -> Result = { lambdaError() }, ) = FakeJoinedRoom( roomNotificationSettingsService = notificationSettingsService, setNameResult = setNameResult, @@ -118,7 +122,7 @@ fun aJoinedRoom( updateCanonicalAliasResult = updateCanonicalAliasResult, publishRoomAliasInRoomDirectoryResult = publishRoomAliasInRoomDirectoryResult, removeRoomAliasFromRoomDirectoryResult = removeRoomAliasFromRoomDirectoryResult, - baseRoom = aRoom( + baseRoom = aFakeBaseRoom( sessionId = sessionId, roomId = roomId, roomPermissions = roomPermissions, @@ -139,5 +143,6 @@ fun aJoinedRoom( joinedMemberCount = joinedMemberCount, activeMemberCount = activeMemberCount, invitedMemberCount = invitedMemberCount, + markAsReadResult = markAsReadResult, ) ) 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 9c116f5a9d..5a845c3331 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 @@ -21,6 +21,8 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.RoomMembersState @@ -28,6 +30,7 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions +import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME @@ -41,7 +44,11 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.powerlevels.FakeRoomPermissions import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore +import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore +import io.element.android.libraries.push.api.notifications.NotificationCleaner +import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.EventsRecorder @@ -79,7 +86,10 @@ class RoomDetailsPresenterTest { analyticsService: AnalyticsService = FakeAnalyticsService(), encryptionService: FakeEncryptionService = FakeEncryptionService(), clipboardHelper: ClipboardHelper = FakeClipboardHelper(), - appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore() + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), + navigator: RoomDetailsNavigator = FakeRoomDetailsNavigator(), + notificationCleaner: NotificationCleaner = FakeNotificationCleaner(), + sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), ): RoomDetailsPresenter { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { @@ -96,6 +106,7 @@ class RoomDetailsPresenterTest { } } return RoomDetailsPresenter( + navigator = navigator, client = matrixClient, room = room, notificationSettingsService = matrixClient.notificationSettingsService, @@ -106,6 +117,8 @@ class RoomDetailsPresenterTest { analyticsService = analyticsService, clipboardHelper = clipboardHelper, appPreferencesStore = appPreferencesStore, + notificationCleaner = notificationCleaner, + sessionPreferencesStore = sessionPreferencesStore, ) } @@ -598,6 +611,85 @@ class RoomDetailsPresenterTest { } } + @Test + fun `present - mark as read`() = runTest { + val markAsReadResult = lambdaRecorder> { _ -> Result.success(Unit) } + val room = aJoinedRoom( + markAsReadResult = markAsReadResult, + ) + val clearMessagesForRoomResult = lambdaRecorder { _, _ -> Result.success(Unit) } + val notificationCleaner = FakeNotificationCleaner( + clearMessagesForRoomLambda = clearMessagesForRoomResult, + ) + val presenter = createRoomDetailsPresenter( + room = room, + notificationCleaner = notificationCleaner, + ) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { + skipItems(1) + with(awaitItem()) { + eventSink(RoomDetailsEvent.MarkAsRead) + } + assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false) + markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ)) + clearMessagesForRoomResult.assertions().isCalledOnce().with( + value(room.sessionId), value(room.roomId) + ) + } + } + + @Test + fun `present - mark as read - private`() = runTest { + val markAsReadResult = lambdaRecorder> { _ -> Result.success(Unit) } + val room = aJoinedRoom( + markAsReadResult = markAsReadResult, + ) + val sessionPreferencesStore = InMemorySessionPreferencesStore( + isSendPublicReadReceiptsEnabled = false, + ) + val clearMessagesForRoomResult = lambdaRecorder { _, _ -> Result.success(Unit) } + val notificationCleaner = FakeNotificationCleaner( + clearMessagesForRoomLambda = clearMessagesForRoomResult, + ) + val presenter = createRoomDetailsPresenter( + room = room, + notificationCleaner = notificationCleaner, + sessionPreferencesStore = sessionPreferencesStore, + ) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { + skipItems(1) + with(awaitItem()) { + eventSink(RoomDetailsEvent.MarkAsRead) + } + assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false) + markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ_PRIVATE)) + clearMessagesForRoomResult.assertions().isCalledOnce().with( + value(room.sessionId), value(room.roomId) + ) + } + } + + @Test + fun `present - mark as unread`() = runTest { + val room = aJoinedRoom() + val onDoneResult = lambdaRecorder { } + val navigator = FakeRoomDetailsNavigator( + onDoneResult = onDoneResult + ) + val presenter = createRoomDetailsPresenter( + room = room, + navigator = navigator, + ) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { + skipItems(1) + with(awaitItem()) { + eventSink(RoomDetailsEvent.MarkAsUnread) + } + onDoneResult.assertions().isCalledOnce() + assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(true) + } + } + private fun roomPermissions( canInvite: Boolean = true, canKick: Boolean = true, From 94392f3ebd260ff8b49c2699c5709163a878517a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 09:16:18 +0000 Subject: [PATCH 200/471] Update kotlin to v2.3.8 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 25aeecd174..9e2b86d7b5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ android_gradle_plugin = "8.13.2" # When updating this, please also update the version in the file ./idea/kotlinc.xml kotlin = "2.3.21" kotlinpoet = "2.3.0" -ksp = "2.3.7" +ksp = "2.3.8" firebaseAppDistribution = "5.2.1" # AndroidX From 42c141109fa4b382010b5c41135c5751a276832c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 20 May 2026 11:32:32 +0200 Subject: [PATCH 201/471] Fix media viewer flickering (#6800) * Fix media viewer flickering This was caused by the data being loaded triggering an index update that got out of sync with the displayed items, and for an instant, the pager index pointed to the wrong data until it was refreshed * Reuse the same 'displayer' function for both forwards and backwards pagination * Make `dataFlow` a property so we don't create a new instance every time we access it * Remove `pageDataComparator` as it prevented new items from being loaded when a pagination returned no valid items to display but has more items to load Make sure we modify the current index when loading new data only if it was pointing to the input event id. * Fix `MediaViewerDataSource` overriding the provided timestamp for `Loading` items Test emitting different loading items from the data source results in the state displaying the different items, so they will trigger a new pagination attempt * Add regression test to check loading -> error -> loading states will still trigger 2 separate `LoadMore` events --- .../impl/datasource/MediaGalleryDataSource.kt | 12 +- .../impl/gallery/MediaGalleryPresenter.kt | 4 +- .../impl/viewer/MediaViewerDataSource.kt | 86 ++++++++----- .../impl/viewer/MediaViewerNode.kt | 2 + .../impl/viewer/MediaViewerPresenter.kt | 104 +++++++-------- .../impl/viewer/MediaViewerState.kt | 2 + .../impl/viewer/MediaViewerView.kt | 9 +- .../viewer/SingleMediaGalleryDataSource.kt | 2 + .../impl/DefaultMediaViewerEntryPointTest.kt | 6 + .../datasource/FakeMediaGalleryDataSource.kt | 12 +- .../impl/viewer/MediaViewerDataSourceTest.kt | 32 +++-- .../impl/viewer/MediaViewerPresenterTest.kt | 120 +++++++++++++----- .../impl/viewer/MediaViewerViewTest.kt | 69 +++++++++- 13 files changed, 312 insertions(+), 148 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt index f5418c76c9..7bdd4872a3 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/MediaGalleryDataSource.kt @@ -32,6 +32,7 @@ import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean interface MediaGalleryDataSource { + val isReady: Boolean fun start(coroutineScope: CoroutineScope) fun groupedMediaItemsFlow(): Flow> fun getLastData(): AsyncData @@ -49,7 +50,7 @@ class TimelineMediaGalleryDataSource( ) : MediaGalleryDataSource { private var timeline: Timeline? = null - private val groupedMediaItemsFlow = MutableSharedFlow>(replay = 1) + private val groupedMediaItemsFlow = MutableSharedFlow>(replay = 1, extraBufferCapacity = 10) override fun groupedMediaItemsFlow(): Flow> = groupedMediaItemsFlow @@ -59,9 +60,12 @@ class TimelineMediaGalleryDataSource( private val isStarted = AtomicBoolean(false) + override val isReady: Boolean get() = isStarted.get() && timeline != null + @OptIn(ExperimentalCoroutinesApi::class) override fun start(coroutineScope: CoroutineScope) { if (!isStarted.compareAndSet(false, true)) { + Timber.w("MediaGalleryDataSource for room ${room.roomId} is already started, ignoring subsequent start call") return } flow { @@ -73,10 +77,12 @@ class TimelineMediaGalleryDataSource( } mediaTimeline.getTimeline().fold( { + Timber.d("Timeline media data source flow started for room ${room.roomId}") timeline = it emit(it) }, { + Timber.e(it, "Failed to get media timeline for room ${room.roomId}") groupedMediaItemsFlow.emit(AsyncData.Failure(it)) }, ) @@ -107,13 +113,13 @@ class TimelineMediaGalleryDataSource( } override suspend fun loadMore(direction: Timeline.PaginationDirection) { - timeline?.paginate(direction) + timeline?.paginate(direction) ?: Timber.w("Timeline is not ready yet, cannot load more media items for room ${room.roomId}") } override suspend fun deleteItem(eventId: EventId) { timeline?.redactEvent( eventOrTransactionId = eventId.toEventOrTransactionId(), reason = null, - ) + ) ?: Timber.w("Timeline is not ready yet, cannot delete media item with eventId $eventId for room ${room.roomId}") } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index e7caa12f2e..2c2fb31020 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -94,7 +94,9 @@ class MediaGalleryPresenter( mode = event.mode } is MediaGalleryEvent.LoadMore -> coroutineScope.launch { - mediaGalleryDataSource.loadMore(event.direction) + if (mediaGalleryDataSource.isReady) { + mediaGalleryDataSource.loadMore(event.direction) + } } is MediaGalleryEvent.Delete -> coroutineScope.launch { mediaGalleryDataSource.deleteItem(event.eventId) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index 01ec68a336..b0b81bbde0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -11,12 +11,13 @@ package io.element.android.libraries.mediaviewer.impl.viewer import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.ProduceStateScope import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.produceState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.extensions.mapCatchingExceptions +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.api.media.MediaSource @@ -37,14 +38,17 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import timber.log.Timber import java.util.concurrent.ConcurrentHashMap class MediaViewerDataSource( mode: MediaViewerMode, + coroutineScope: CoroutineScope, private val dispatcher: CoroutineDispatcher, private val galleryDataSource: MediaGalleryDataSource, private val mediaLoader: MatrixMediaLoader, @@ -76,40 +80,58 @@ class MediaViewerDataSource( localMediaStates.clear() } + /** + * Helper function to translate the [dataFlow] result to a Compose [State] that can be observed in the UI. + */ @Composable - fun collectAsState(): State> { - return remember { dataFlow() }.collectAsState(initialData()) + fun produceState( + producer: suspend ProduceStateScope>.(StateFlow>) -> Unit + ): State> { + return produceState(initialValue = initialData()) { + producer(dataFlow) + } + } + + /** + * Find the index of the page corresponding to the given eventId, or null if not found. + */ + fun findEventIndex(eventId: EventId?): Int? { + if (eventId == null) return null + return dataFlow.value.indexOfFirst { (it as? MediaViewerPageData.MediaViewerData)?.eventId == eventId }.takeIf { it >= 0 } } @VisibleForTesting - internal fun dataFlow(): Flow> { - return galleryDataSource.groupedMediaItemsFlow() - .map { groupedItems -> - when (groupedItems) { - AsyncData.Uninitialized, - is AsyncData.Loading -> { - persistentListOf( - MediaViewerPageData.Loading( - direction = Timeline.PaginationDirection.BACKWARDS, - timestamp = systemClock.epochMillis(), - pagerKey = Long.MIN_VALUE, - ) + internal val dataFlow: StateFlow> = galleryDataSource.groupedMediaItemsFlow() + .map { groupedItems -> + when (groupedItems) { + AsyncData.Uninitialized, + is AsyncData.Loading -> { + persistentListOf( + MediaViewerPageData.Loading( + direction = Timeline.PaginationDirection.BACKWARDS, + timestamp = systemClock.epochMillis(), + pagerKey = Long.MIN_VALUE, ) - } - is AsyncData.Failure -> { - persistentListOf( - MediaViewerPageData.Failure(groupedItems.error), - ) - } - is AsyncData.Success -> { - withContext(dispatcher) { - val mediaItems = groupedItems.data.getItems(galleryMode) - buildMediaViewerPageList(mediaItems) - } + ) + } + is AsyncData.Failure -> { + persistentListOf( + MediaViewerPageData.Failure(groupedItems.error), + ) + } + is AsyncData.Success -> { + withContext(dispatcher) { + val mediaItems = groupedItems.data.getItems(galleryMode) + buildMediaViewerPageList(mediaItems) } } } - } + } + .stateIn( + scope = CoroutineScope(coroutineScope.coroutineContext + dispatcher), + started = SharingStarted.Lazily, + initialValue = initialData(), + ) private fun initialData(): ImmutableList { val initialMediaItems = @@ -148,7 +170,7 @@ class MediaViewerDataSource( is MediaItem.LoadingIndicator -> add( MediaViewerPageData.Loading( direction = mediaItem.direction, - timestamp = systemClock.epochMillis(), + timestamp = mediaItem.timestamp, pagerKey = pagerKeysHandler.getKey(mediaItem), ) ) @@ -161,7 +183,9 @@ class MediaViewerDataSource( } suspend fun loadMore(direction: Timeline.PaginationDirection) { - galleryDataSource.loadMore(direction) + if (galleryDataSource.isReady) { + galleryDataSource.loadMore(direction) + } } suspend fun loadMedia(data: MediaViewerPageData.MediaViewerData) { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt index d834534e3e..e594daeb41 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNode.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -118,6 +119,7 @@ class MediaViewerNode( navigator = this, dataSource = MediaViewerDataSource( mode = inputs.mode, + coroutineScope = lifecycleScope, dispatcher = coroutineDispatchers.computation, galleryDataSource = mediaGallerySource, mediaLoader = mediaLoader, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index d40026dd37..296f20ef4c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -14,14 +14,12 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.IntState import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject @@ -45,10 +43,7 @@ import io.element.android.libraries.mediaviewer.impl.model.mediaPermissions import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import io.element.android.libraries.androidutils.R as UtilsR @@ -75,12 +70,27 @@ class MediaViewerPresenter( @Composable override fun present(): MediaViewerState { val coroutineScope = rememberCoroutineScope() - val data = dataSource.collectAsState() - val currentIndex = remember { mutableIntStateOf(searchIndex(data.value, inputs.eventId)) } + val currentIndex = remember { mutableIntStateOf(dataSource.findEventIndex(inputs.eventId) ?: 0) } + val data = dataSource.produceState { flow -> + flow.collectLatest { new -> + val existingItem = value.getOrNull(currentIndex.intValue) + val newItem = new.getOrNull(currentIndex.intValue) + if (existingItem is MediaViewerPageData.MediaViewerData && existingItem.eventId == inputs.eventId && newItem != existingItem) { + currentIndex.intValue = dataSource.findEventIndex(inputs.eventId) ?: 0 + } else if (currentIndex.intValue > 0 && value.firstOrNull() is MediaViewerPageData.Loading && + new.firstOrNull() !is MediaViewerPageData.Loading) { + // Restore index based on the eventId after the initial items have been loaded + currentIndex.intValue = dataSource.findEventIndex(inputs.eventId) ?: 0 + } + value = new + } + } + val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() - NoMoreItemsBackwardSnackBarDisplayer(currentIndex, data) - NoMoreItemsForwardSnackBarDisplayer(currentIndex, data) + // Add both forward and backward pagination state checks to display a snackbar when there is no more items to load in either direction + NoMoreItemsSnackBarDisplayer(currentIndex, data, Timeline.PaginationDirection.FORWARDS) + NoMoreItemsSnackBarDisplayer(currentIndex, data, Timeline.PaginationDirection.BACKWARDS) val permissions by room.permissionsAsState(MediaPermissions.DEFAULT) { perms -> perms.mediaPermissions() @@ -176,52 +186,39 @@ class MediaViewerPresenter( } @Composable - private fun NoMoreItemsBackwardSnackBarDisplayer( + private fun NoMoreItemsSnackBarDisplayer( currentIndex: IntState, data: State>, + direction: Timeline.PaginationDirection, ) { - // With newest-first ordering, backward loading indicator is at the last index - val isRenderingLoadingBackward by remember { - derivedStateOf { - currentIndex.intValue == data.value.lastIndex && - data.value.size > 1 && - data.value.lastOrNull() is MediaViewerPageData.Loading + var previousIndex by remember { mutableIntStateOf(currentIndex.intValue) } + var previousDataSize by remember { mutableIntStateOf(data.value.size) } + var wasLoading: Boolean? by remember { mutableStateOf(null) } + LaunchedEffect(currentIndex.intValue, data.value) { + fun isLoading(index: Int, data: List, direction: Timeline.PaginationDirection): Boolean { + return when (direction) { + Timeline.PaginationDirection.BACKWARDS -> index == data.lastIndex && data.lastOrNull() is MediaViewerPageData.Loading + Timeline.PaginationDirection.FORWARDS -> index == 0 && data.firstOrNull() is MediaViewerPageData.Loading + } } - } - if (isRenderingLoadingBackward) { - LaunchedEffect(Unit) { - // Observe the loading data vanishing - snapshotFlow { data.value.lastOrNull() is MediaViewerPageData.Loading } - .distinctUntilChanged() - .filter { !it } - .onEach { showNoMoreItemsSnackbar() } - .launchIn(this) + // Reset the effect when the user navigate to another item so we only take into account index changes caused by data changes + if (previousIndex != currentIndex.intValue) { + wasLoading = null + previousIndex = currentIndex.intValue + } + // If we were navigating backwards and the data size grew, we can discard the previous value: it means we received new items + if (direction == Timeline.PaginationDirection.BACKWARDS && previousDataSize < data.value.size) { + wasLoading = null } - } - } - @Composable - private fun NoMoreItemsForwardSnackBarDisplayer( - currentIndex: IntState, - data: State>, - ) { - // With newest-first ordering, forward loading indicator is at the first index - val isRenderingLoadingForward by remember { - derivedStateOf { - currentIndex.intValue == 0 && - data.value.size > 1 && - data.value.firstOrNull() is MediaViewerPageData.Loading - } - } - if (isRenderingLoadingForward) { - LaunchedEffect(Unit) { - // Observe the loading data vanishing - snapshotFlow { data.value.firstOrNull() is MediaViewerPageData.Loading } - .distinctUntilChanged() - .filter { !it } - .onEach { showNoMoreItemsSnackbar() } - .launchIn(this) + val isLoading = isLoading(currentIndex.intValue, data.value, direction) + + if (wasLoading == true && !isLoading) { + showNoMoreItemsSnackbar() } + + previousDataSize = data.value.size + wasLoading = isLoading } } @@ -293,13 +290,4 @@ class MediaViewerPresenter( CommonStrings.error_unknown } } - - private fun searchIndex(data: List, eventId: EventId?): Int { - if (eventId == null) { - return 0 - } - return data.indexOfFirst { - (it as? MediaViewerPageData.MediaViewerData)?.eventId == eventId - }.coerceAtLeast(0) - } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt index 8a4da2aac3..a51d3c44e9 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.mediaviewer.impl.viewer +import androidx.compose.runtime.Immutable import androidx.compose.runtime.State import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage @@ -29,6 +30,7 @@ data class MediaViewerState( val eventSink: (MediaViewerEvent) -> Unit, ) +@Immutable sealed interface MediaViewerPageData { val pagerKey: Long diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 5e9ced7d77..d52db124ea 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -44,7 +44,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -179,9 +178,11 @@ fun MediaViewerView( val pagerState = rememberPagerState(state.currentIndex, 0f) { state.listData.size } - LaunchedEffect(pagerState) { - snapshotFlow { pagerState.currentPage }.collect { page -> - state.eventSink(MediaViewerEvent.OnNavigateTo(page)) + + LaunchedEffect(pagerState.targetPage, state.currentIndex) { + // Only emit an index navigation change when it's triggered by the user scrolling + if (pagerState.targetPage != state.currentIndex && pagerState.isScrollInProgress) { + state.eventSink(MediaViewerEvent.OnNavigateTo(pagerState.targetPage)) } } HorizontalPager( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt index 7bbf397171..146ace8620 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt @@ -30,6 +30,8 @@ class SingleMediaGalleryDataSource( override fun groupedMediaItemsFlow() = flowOf(AsyncData.Success(data)) override fun getLastData(): AsyncData = AsyncData.Success(data) + override val isReady: Boolean = true + override suspend fun loadMore(direction: Timeline.PaginationDirection) = Unit override suspend fun deleteItem(eventId: EventId) = Unit diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt index b848ea2a7b..e7d45f433a 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt @@ -33,16 +33,21 @@ import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain import org.junit.Rule import org.junit.Test +@OptIn(ExperimentalCoroutinesApi::class) class DefaultMediaViewerEntryPointTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @Test fun `test node builder`() = runTest { + Dispatchers.setMain(testCoroutineDispatchers().main) val entryPoint = DefaultMediaViewerEntryPoint() val mockMediaUri: Uri = mockk("localMediaUri") val localMediaFactory = FakeLocalMediaFactory(mockMediaUri) @@ -89,6 +94,7 @@ class DefaultMediaViewerEntryPointTest { @Test fun `test node builder avatar`() = runTest { + Dispatchers.setMain(testCoroutineDispatchers().main) val entryPoint = DefaultMediaViewerEntryPoint() val mockMediaUri: Uri = mockk("localMediaUri") val localMediaFactory = FakeLocalMediaFactory(mockMediaUri) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt index 4a64f02a33..5839f33ce6 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/datasource/FakeMediaGalleryDataSource.kt @@ -15,18 +15,20 @@ import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems import io.element.android.tests.testutils.lambda.lambdaError import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow class FakeMediaGalleryDataSource( + initialData: AsyncData = AsyncData.Uninitialized, + private val isReadyResult: () -> Boolean = { true }, private val startLambda: () -> Unit = { lambdaError() }, private val loadMoreLambda: (Timeline.PaginationDirection) -> Unit = { lambdaError() }, private val deleteItemLambda: (EventId) -> Unit = { lambdaError() }, - ) : MediaGalleryDataSource { +) : MediaGalleryDataSource { override fun start(coroutineScope: CoroutineScope) = startLambda() - private val groupedMediaItemsFlow = MutableSharedFlow>( - replay = 1 - ) + private val groupedMediaItemsFlow = MutableStateFlow(initialData) + + override val isReady: Boolean get() = isReadyResult() override fun groupedMediaItemsFlow(): Flow> { return groupedMediaItemsFlow diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt index c3f1ab9a0d..d7ba547ee4 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSourceTest.kt @@ -62,15 +62,20 @@ class MediaViewerDataSourceTest { @Test fun `test dataFlow uninitialized, loading and error`() = runTest { - val galleryDataSource = FakeMediaGalleryDataSource() + val galleryDataSource = FakeMediaGalleryDataSource( + initialData = AsyncData.Uninitialized, + ) val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + // The flow starts with an empty result + assertThat(awaitItem()).isEmpty() galleryDataSource.emitGroupedMediaItems(AsyncData.Uninitialized) assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java) galleryDataSource.emitGroupedMediaItems(AsyncData.Loading()) - assertThat(awaitItem().first()).isInstanceOf(MediaViewerPageData.Loading::class.java) + // No items emitted, we were already loading data + ensureAllEventsConsumed() galleryDataSource.emitGroupedMediaItems(AsyncData.Failure(AN_EXCEPTION)) assertThat(awaitItem().first()).isEqualTo(MediaViewerPageData.Failure(AN_EXCEPTION)) } @@ -82,7 +87,7 @@ class MediaViewerDataSourceTest { val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -102,7 +107,8 @@ class MediaViewerDataSourceTest { val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -141,7 +147,8 @@ class MediaViewerDataSourceTest { mode = MediaViewerMode.TimelineImagesAndVideos(timelineMode = Timeline.Mode.Media), galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -163,7 +170,8 @@ class MediaViewerDataSourceTest { mode = MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.Media), galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -184,7 +192,8 @@ class MediaViewerDataSourceTest { val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -217,7 +226,8 @@ class MediaViewerDataSourceTest { val sut = createMediaViewerDataSource( galleryDataSource = galleryDataSource, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -241,7 +251,8 @@ class MediaViewerDataSourceTest { galleryDataSource = galleryDataSource, mediaLoader = mediaLoader, ) - sut.dataFlow().test { + sut.dataFlow.test { + skipItems(1) galleryDataSource.emitGroupedMediaItems( AsyncData.Success( aGroupedMediaItems( @@ -271,6 +282,7 @@ class MediaViewerDataSourceTest { mediaLoader: MatrixMediaLoader = FakeMatrixMediaLoader(), localMediaFactory: LocalMediaFactory = FakeLocalMediaFactory(mockMediaUrl), ) = MediaViewerDataSource( + coroutineScope = backgroundScope, mode = mode, dispatcher = testCoroutineDispatchers().computation, galleryDataSource = galleryDataSource, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index 86689859ad..459d30b8d5 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader @@ -51,6 +52,8 @@ import io.mockk.mockk import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -97,6 +100,8 @@ class MediaViewerPresenterTest { assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) + + cancelAndIgnoreRemainingEvents() } } @@ -120,6 +125,8 @@ class MediaViewerPresenterTest { assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isFalse() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) + + cancelAndIgnoreRemainingEvents() } } @@ -143,6 +150,8 @@ class MediaViewerPresenterTest { assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) + + cancelAndIgnoreRemainingEvents() } } @@ -167,6 +176,8 @@ class MediaViewerPresenterTest { assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) + + cancelAndIgnoreRemainingEvents() } } @@ -555,6 +566,8 @@ class MediaViewerPresenterTest { ) val finalState = awaitItem() assertThat(finalState.currentIndex).isEqualTo(1) + + cancelAndIgnoreRemainingEvents() } } @@ -578,32 +591,34 @@ class MediaViewerPresenterTest { mode: MediaViewerEntryPoint.MediaViewerMode, expectedSnackbarResId: Int, ) = runTest { + val image = anImage.copy(eventId = AN_EVENT_ID) val mediaGalleryDataSource = FakeMediaGalleryDataSource( + initialData = AsyncData.Success( + if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { + GroupedMediaItems( + imageAndVideoItems = persistentListOf(), + fileItems = persistentListOf(aForwardLoadingIndicator, image, aBackwardLoadingIndicator), + ) + } else { + GroupedMediaItems( + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, image, aBackwardLoadingIndicator), + fileItems = persistentListOf(), + ) + } + ), startLambda = { }, ) val presenter = createMediaViewerPresenter( + eventId = AN_EVENT_ID, localMediaFactory = localMediaFactory, mode = mode, mediaGalleryDataSource = mediaGalleryDataSource, ) presenter.test { - awaitFirstItem() - mediaGalleryDataSource.emitGroupedMediaItems( - AsyncData.Success( - if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { - GroupedMediaItems( - imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), - ) - } else { - GroupedMediaItems( - imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), - fileItems = persistentListOf(), - ) - } - ) - ) - val updatedState = awaitItem() + val updatedState = awaitFirstItem() + + advanceUntilIdle() + runCurrent() // User navigate to the first item (forward loading indicator) updatedState.eventSink( MediaViewerEvent.OnNavigateTo(0) @@ -614,17 +629,17 @@ class MediaViewerPresenterTest { if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) { GroupedMediaItems( imageAndVideoItems = persistentListOf(), - fileItems = persistentListOf(anImage, aBackwardLoadingIndicator), + fileItems = persistentListOf(image, aBackwardLoadingIndicator), ) } else { GroupedMediaItems( - imageAndVideoItems = persistentListOf(anImage, aBackwardLoadingIndicator), + imageAndVideoItems = persistentListOf(image, aBackwardLoadingIndicator), fileItems = persistentListOf(), ) } ) ) - skipItems(1) + skipItems(2) val stateWithSnackbar = awaitItem() assertThat(stateWithSnackbar.snackbarMessage!!.messageResId).isEqualTo(expectedSnackbarResId) } @@ -707,21 +722,19 @@ class MediaViewerPresenterTest { fun `present - no snackbar displayed when there is no more items but not displaying a loading item`() = runTest { val mediaGalleryDataSource = FakeMediaGalleryDataSource( startLambda = { }, + initialData = AsyncData.Success( + GroupedMediaItems( + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, anImage.copy(eventId = AN_EVENT_ID_2), aBackwardLoadingIndicator), + fileItems = persistentListOf(), + ) + ) ) val presenter = createMediaViewerPresenter( + eventId = AN_EVENT_ID, localMediaFactory = localMediaFactory, mediaGalleryDataSource = mediaGalleryDataSource, ) presenter.test { - awaitFirstItem() - mediaGalleryDataSource.emitGroupedMediaItems( - AsyncData.Success( - GroupedMediaItems( - imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), - fileItems = persistentListOf(), - ) - ) - ) val updatedState = awaitItem() // User navigate to the media updatedState.eventSink( @@ -774,6 +787,51 @@ class MediaViewerPresenterTest { } } + @Test + fun `present - receiving loading items with different timestamps emits different items too`() = runTest { + val loadMoreLambda = lambdaRecorder { } + val mediaGalleryDataSource = FakeMediaGalleryDataSource( + startLambda = { }, + loadMoreLambda = loadMoreLambda, + ) + val presenter = createMediaViewerPresenter( + localMediaFactory = localMediaFactory, + mediaGalleryDataSource = mediaGalleryDataSource, + ) + val anImage = aMediaItemImage( + mediaSourceUrl = aUrl, + ) + presenter.test { + awaitFirstItem() + mediaGalleryDataSource.emitGroupedMediaItems( + AsyncData.Success( + GroupedMediaItems( + imageAndVideoItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator), + fileItems = persistentListOf(), + ) + ) + ) + val updatedState = awaitItem() + + // Get the exact same items, but with new timestamps for the loading indicators + mediaGalleryDataSource.emitGroupedMediaItems( + AsyncData.Success( + GroupedMediaItems( + imageAndVideoItems = persistentListOf( + aForwardLoadingIndicator.copy(timestamp = 1234L), + anImage, + aBackwardLoadingIndicator.copy(timestamp = 1234L), + ), + fileItems = persistentListOf(), + ) + ) + ) + + // We should get a new list of items, which should not be equal to the previous one + assertThat(updatedState.listData).isNotEqualTo(awaitItem().listData) + } + } + @Test fun `present - view in timeline hides the bottom sheet and invokes the navigator`() = runTest { val onViewInTimelineClickLambda = lambdaRecorder { } @@ -794,6 +852,7 @@ class MediaViewerPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(MediaViewerEvent.OpenInfo(aMediaViewerPageData())) + skipItems(1) val withBottomSheetState = awaitItem() assertThat(withBottomSheetState.mediaBottomSheetState).isInstanceOf(MediaBottomSheetState.Details::class.java) initialState.eventSink(MediaViewerEvent.ViewInTimeline(AN_EVENT_ID)) @@ -823,6 +882,7 @@ class MediaViewerPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(MediaViewerEvent.OpenInfo(aMediaViewerPageData())) + skipItems(1) val withBottomSheetState = awaitItem() assertThat(withBottomSheetState.mediaBottomSheetState).isInstanceOf(MediaBottomSheetState.Details::class.java) initialState.eventSink(MediaViewerEvent.Forward(AN_EVENT_ID)) @@ -854,6 +914,7 @@ class MediaViewerPresenterTest { presenter.test { val initialState = awaitItem() initialState.eventSink(MediaViewerEvent.OpenInfo(aMediaViewerPageData())) + skipItems(1) val withBottomSheetState = awaitItem() assertThat(withBottomSheetState.mediaBottomSheetState).isInstanceOf(MediaBottomSheetState.Details::class.java) initialState.eventSink(MediaViewerEvent.Forward(AN_EVENT_ID)) @@ -892,6 +953,7 @@ internal fun TestScope.createMediaViewerPresenter( ), navigator = mediaViewerNavigator, dataSource = MediaViewerDataSource( + coroutineScope = backgroundScope, mode = mode, dispatcher = testCoroutineDispatchers().computation, galleryDataSource = mediaGalleryDataSource, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index b426ca50ca..6521f450f5 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -13,6 +13,11 @@ package io.element.android.libraries.mediaviewer.impl.viewer import android.net.Uri import androidx.activity.ComponentActivity import androidx.annotation.StringRes +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertHasClickAction @@ -23,6 +28,7 @@ import androidx.compose.ui.test.swipeDown import androidx.compose.ui.test.v2.runAndroidComposeUiTest import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.impl.details.aMediaBottomSheetStateDetails import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.libraries.ui.strings.CommonStrings @@ -33,9 +39,11 @@ import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack import io.element.android.tests.testutils.setSafeContent import io.mockk.mockk +import kotlinx.coroutines.delay import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config +import kotlin.time.Duration.Companion.milliseconds @RunWith(AndroidJUnit4::class) class MediaViewerViewTest { @@ -60,7 +68,6 @@ class MediaViewerViewTest { } eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(state.listData.first() as MediaViewerPageData.MediaViewerData), ) ) @@ -122,7 +129,6 @@ class MediaViewerViewTest { onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(data), expectedEvent, ) @@ -178,7 +184,6 @@ class MediaViewerViewTest { clickOn(textRes) eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(data), expectedEvent, ) @@ -208,7 +213,6 @@ class MediaViewerViewTest { .assertDoesNotExist() eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(state.listData.first() as MediaViewerPageData.MediaViewerData), ) ) @@ -231,7 +235,6 @@ class MediaViewerViewTest { } eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(state.listData.first() as MediaViewerPageData.MediaViewerData), ) ) @@ -256,7 +259,6 @@ class MediaViewerViewTest { clickOn(CommonStrings.action_retry) eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(data), MediaViewerEvent.LoadMedia(data), ) @@ -282,12 +284,65 @@ class MediaViewerViewTest { clickOn(CommonStrings.action_cancel) eventsRecorder.assertList( listOf( - MediaViewerEvent.OnNavigateTo(0), MediaViewerEvent.LoadMedia(data), MediaViewerEvent.ClearLoadingError(data) ) ) } + + @Test + fun `loading event after an error triggers load more Event`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + val states = listOf( + aMediaViewerState( + listData = listOf(aMediaViewerPageDataLoading(timestamp = 0L)), + eventSink = eventsRecorder, + ), + aMediaViewerState( + listData = listOf(MediaViewerPageData.Failure(IllegalStateException("error"))), + eventSink = eventsRecorder, + ), + aMediaViewerState( + listData = listOf(aMediaViewerPageDataLoading(timestamp = 0L)), + eventSink = eventsRecorder, + ), + // This one should be ignored since it has the same timestamp as the last one, it should not trigger a recomposition + aMediaViewerState( + listData = listOf(aMediaViewerPageDataLoading(timestamp = 0L)), + eventSink = eventsRecorder, + ), + ) + setSafeContent { + // Iterate over the states with a delay to give the view some time to trigger the `LoadMore` Event + var state by remember { mutableStateOf(states.first()) } + LaunchedEffect(Unit) { + val iterator = states.iterator() + while (iterator.hasNext()) { + delay(200.milliseconds) + state = iterator.next() + } + } + MediaViewerView( + state = state, + textFileViewer = { _, _ -> }, + onBackClick = EnsureNeverCalled(), + audioFocus = null, + ) + } + + // Advance time to let the states update + mainClock.advanceTimeBy(3_000) + + // `LoadMore` should be called twice, once for the first loading state, and once for the second one even though they have the same timestamp because + // of the intermediate error state. + // The third one will be ignored since it has the same timestamp as the second one and it'll be discarded by the Compose's equality diffing. + eventsRecorder.assertList( + listOf( + MediaViewerEvent.LoadMore(direction = Timeline.PaginationDirection.BACKWARDS), + MediaViewerEvent.LoadMore(direction = Timeline.PaginationDirection.BACKWARDS), + ) + ) + } } private fun AndroidComposeUiTest.setMediaViewerView( From 64d933690180126f2fab1c1d964aa780d4a0cb1c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 20 May 2026 14:26:44 +0200 Subject: [PATCH 202/471] Fix 'Conversation label cannot be empty' error (#6823) This happens when building a `ShortcutInfoCompat` in `DefaultNotificationConversationService.onSendMessage` when the provided room name is not null but it's empty. --- .../impl/messagecomposer/MessageComposerPresenter.kt | 2 +- .../conversations/NotificationConversationService.kt | 2 +- .../DefaultNotificationConversationService.kt | 7 ++++--- .../conversations/FakeNotificationConversationService.kt | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index d93fe0ee86..b42cda7dfc 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 @@ -571,7 +571,7 @@ class MessageComposerPresenter( notificationConversationService.onSendMessage( sessionId = room.sessionId, roomId = roomInfo.id, - roomName = roomInfo.name ?: roomInfo.id.value, + roomName = roomInfo.name, roomIsDirect = roomInfo.isDm, roomAvatarUrl = roomInfo.avatarUrl ?: roomMembers.getDirectRoomMember(roomInfo = roomInfo, sessionId = room.sessionId)?.avatarUrl, ) diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt index 504adacdb6..4ea80e4ce8 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/conversations/NotificationConversationService.kt @@ -22,7 +22,7 @@ interface NotificationConversationService { suspend fun onSendMessage( sessionId: SessionId, roomId: RoomId, - roomName: String, + roomName: String?, roomIsDirect: Boolean, roomAvatarUrl: String?, ) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt index ce20234385..f898e4a9ed 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt @@ -76,7 +76,7 @@ class DefaultNotificationConversationService( override suspend fun onSendMessage( sessionId: SessionId, roomId: RoomId, - roomName: String, + roomName: String?, roomIsDirect: Boolean, roomAvatarUrl: String?, ) { @@ -93,10 +93,11 @@ class DefaultNotificationConversationService( val imageLoader = imageLoaderHolder.get(client) val defaultShortcutIconSize = ShortcutManagerCompat.getIconMaxWidth(context) + val name = roomName?.takeIf { it.isNotBlank() } ?: roomId.value val icon = bitmapLoader.getRoomBitmap( avatarData = AvatarData( id = roomId.value, - name = roomName, + name = name, url = roomAvatarUrl, size = AvatarSize.RoomDetailsHeader, ), @@ -105,7 +106,7 @@ class DefaultNotificationConversationService( )?.let(IconCompat::createWithBitmap) val shortcutInfo = ShortcutInfoCompat.Builder(context, createShortcutId(sessionId, roomId)) - .setShortLabel(roomName) + .setShortLabel(name) .setIcon(icon) .setIntent(intentProvider.getViewRoomIntent(sessionId, roomId, threadId = null, eventId = null)) .setCategories(categories) diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/conversations/FakeNotificationConversationService.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/conversations/FakeNotificationConversationService.kt index 0c8d870448..a2022ea22d 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/conversations/FakeNotificationConversationService.kt +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/conversations/FakeNotificationConversationService.kt @@ -16,7 +16,7 @@ class FakeNotificationConversationService : NotificationConversationService { override suspend fun onSendMessage( sessionId: SessionId, roomId: RoomId, - roomName: String, + roomName: String?, roomIsDirect: Boolean, roomAvatarUrl: String?, ) = Unit From 3d85cf4fd24dbc32d0e4f3bfc5b24f07a6602005 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 14:27:38 +0200 Subject: [PATCH 203/471] Format --- .../features/roomdetails/impl/RoomDetailsPresenterTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 5a845c3331..51a6f97ab0 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 @@ -633,7 +633,8 @@ class RoomDetailsPresenterTest { assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false) markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ)) clearMessagesForRoomResult.assertions().isCalledOnce().with( - value(room.sessionId), value(room.roomId) + value(room.sessionId), + value(room.roomId), ) } } @@ -664,7 +665,8 @@ class RoomDetailsPresenterTest { assertThat(room.baseRoom.setUnreadFlagCalls).containsExactly(false) markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ_PRIVATE)) clearMessagesForRoomResult.assertions().isCalledOnce().with( - value(room.sessionId), value(room.roomId) + value(room.sessionId), + value(room.roomId), ) } } From 260ea4a7d26546d6ca93ee01136ffbcd508ace39 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 20 May 2026 12:44:42 +0000 Subject: [PATCH 204/471] Update screenshots --- .../images/features.roomdetails.impl_RoomDetailsA11y_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_0_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_10_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_11_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_12_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_13_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_14_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_15_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_16_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_17_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_18_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_19_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_1_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_20_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_21_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_22_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_2_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_3_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_4_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_5_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_6_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_7_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_8_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_9_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_0_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_10_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_11_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_12_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_13_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_14_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_15_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_16_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_17_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_18_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_19_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_1_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_20_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_21_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_22_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_2_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_3_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_4_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_5_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_6_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_7_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_8_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_9_en.png | 4 ++-- 47 files changed, 94 insertions(+), 94 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png index 43a49f7320..25302db21d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34ce2f0451fb1681648c91771316256e67b1f295f4deb97f6115ae593d82f2ef -size 83472 +oid sha256:d7c187e173ae6d02d6c95c03bcc24cce91b0922e6aa5a08677732748aab0fc30 +size 85320 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png index bb68ee4d65..739ff5f268 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eeb2355a693a7ac522028833fea4f0506e1b455e4aee888ed314c2700a9a87af -size 45454 +oid sha256:8979ec6209ad6e0deb6946e00ce227f3db41a565218afe31b72b889bee224daa +size 46268 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png index 77bc91df06..7609f5e725 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b45d35b68d232fc5cbaf3b01156127a77618bcf2197375ad42f61bc7ad8ec400 -size 44161 +oid sha256:16200632461dcbbf0f6211b7064b61a1e2fa04cd34e14946beb12b9e9f4d7b7f +size 43901 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png index 9f23841e1d..ef1954720c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a7efaa997737bfc6db898987b78077ec3c8039a6d0cce84d770bcabcbdc815e -size 43161 +oid sha256:2279893a284e620cf487f6370982a14278fd57bb278e53932a5fd7c5cab8e95e +size 43033 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png index 308c764795..a0cee002e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8de9a9297383ff1ec552304a49040c44236654dd16ba9ae5f54fddf3e14c617 -size 44764 +oid sha256:5d4da60a94c56b13b65efb797825c54c1e107fe4440f56b1fcac5d47779cbf64 +size 44502 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png index 13403ea92d..eb330780d8 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff4c856028887c1390df7b313462e37e0723ff98b9f92aa769b2fc9133ab6ec5 -size 44673 +oid sha256:ee2899f7870b09f287b45b327d5d0462b423ba65afa1087dec1be862507378de +size 44411 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png index 59ac3c53af..e09c2dfd95 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:162a9cd8b10b8ec7c33dc926c63d2d04986ca0e29a6cd036771ffc137176e28c -size 45079 +oid sha256:78ba2ddfdfc47febc0503daa0985a5fdf04ef15bdc391b4a88c3c630b1946b35 +size 46004 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png index 9d934ba726..cc49f40494 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fcd3e86c18e34e96a2b535340a27a8cb248d48697ce75d1c7c62cde27b3e2ccb -size 45577 +oid sha256:4a832b0e395498b15e176529ea55c50ed129f6d2330fbec4ff1f8e3dcaeab93f +size 46543 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png index cfd42f8af2..e8c0ec1046 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5a20887fa9a6dd7c96f328f24608e1fcea0326b60cf8a7b43e2387fa19595f5 -size 44932 +oid sha256:d8e738bd873339d195b4190f65a18016f2f0eb1bbc9f513a2b25467cb5f5828c +size 44745 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png index b45fd6c6f1..5098ecb3a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8087a634dd7bf3893697d8ab599f9694169e878b4484b0d8e9821307a8423d63 -size 44243 +oid sha256:892dd301ab08c6a302e8286d3f86878178130a22baf550402f2102ba98f0d21c +size 43982 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png index 9f56dab8f3..899ef403a4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f239cb428d2e4cffa86e8d8397904af0c0c5e621585f31bcf3135cdd2c81a40 -size 40541 +oid sha256:809c597757d6f1385803c1378a494f24309b533168df59c7e06852c1ce34b8d3 +size 41408 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png index 81bb7961e9..0625fcfe05 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bcc557108fc16a1f63d22b3512d271d30dccc801838607e78921baf9ba490c0 -size 40509 +oid sha256:3756793e5d92f0fac18e96a49e69ac7ed6901c1195d3e6526088027405e82dbf +size 41361 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png index 6b89622e9d..346c9cf052 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7b4d5f81f605a0858994e50d72fbd17d2f82e0b2e4673ff9baf6e25a103ef55 -size 40392 +oid sha256:099f571485ce8476d4b8a2313610b6c1d03a7d7420132e06d6258b483d640a08 +size 39130 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png index b0a44326fd..a58193c18f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fef854b4c908e206ece79491c655fbf0fd5b51883f3d77045c579664331b9058 -size 45195 +oid sha256:66fd2cfb9f343c77b8db0a4fbc0d7a1571a4dfe1d70c78823a4ba03bfbc846eb +size 44934 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png index 637549dfc7..37bb88b0fb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dde778a13f36958fefba950fe0d3470b7277d22fe4eac189c77b0838b3728ef3 -size 44940 +oid sha256:465ecd337f705262c8f533b3b01daa6a548c6ee88752be405609660ddfaf56b1 +size 44676 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png index cafc853f58..6356cec349 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5870cd12f6de78ecf72a9dc8e3716e0e3476ecf2f29647fc1eca900f5e030f9 -size 44652 +oid sha256:3cca233c171b6c6709a94163ca1f0024a42ff66200383fdbcaf57b321251522e +size 44390 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png index 12efcf7526..3846aec585 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1281394671737e78232cb7d9785b57b8ef2ca50c125f0c0854d101d27efaa939 -size 37910 +oid sha256:9e5e8b39197d115c6190b89fd54033b3319b36dfbf5c6f152dd84e6c79147bbb +size 37877 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png index 3d6f26c63c..08ca4ad1e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec2b5711ea9ffacc6aad4f7b16efdcc78603e9eaa9d33abfaa0d867db5aead67 -size 42355 +oid sha256:4899d2ffcc6da1f414f7fb223440b8463250dc5ac07558cfaee2947f95c93362 +size 42236 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png index 90dd2364d3..062c440463 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4adfa037ff619098067d03f252ba0af1c3c89553ea0c81dcda41f7f0b655baad -size 42450 +oid sha256:577381a33e7f0a88ceebb7f2be6528c3e99417cf1351b74c134aab948664cc64 +size 42149 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png index 2d3d2f0a74..1ff34bd6e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76f63f6c98318762a574d7fb04c8e23ad8312b1fc9adc404669ac6d9e2c42097 -size 40127 +oid sha256:b2bc2699d8dcf770103726c571a6966eacf173cbbad05c4fa6188815aa9ae231 +size 41052 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png index bca90757f7..c1decbd918 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3063251e98ea6e797e5178cb954d99e71fc1422df19c24a59b1395531380e941 -size 41113 +oid sha256:9cba49398954a404def6b37f3cde057cb88d6f59249b943f1689403ccd902326 +size 42024 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png index fc66173002..379f61db22 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d5b8a9f7bc2e7f654b018b2f66b5971c21326dd6b64371f330299649daea4c0 -size 45755 +oid sha256:405a29c158295616560152c5790f189a8019bd635a53a69b581c8aa595e6e9ea +size 45494 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png index d4ff65b44f..d0ffc70f42 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2dd6b524bd146b4e66621fea59515e6784055f52f6d1d08c393b5be208e5f29a -size 44657 +oid sha256:3f948e9cc2eb7bc73b0441fbdea72d582a042895b3404c8302b7e182c3481fae +size 44425 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png index 4264857e69..2894ac3c71 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73a27b8df68182a3d0ae4872a14688ae10e5651ec85490c1e46186acc2c60028 -size 44671 +oid sha256:b4702b18ad5d203050140e6e1a18553344f52eb5482fcaf6ab497698b9ec59fd +size 44432 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png index f0751a53cd..afabfcecf5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bedc6d0b55d94586de728cb4039303c9ecbd8b3a090c401331ae1bf6e0ba426 -size 46396 +oid sha256:4189b58dd1263a81566ea86d9881c02f053ecb2a91ba9442553fbe0f230e6860 +size 47105 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png index f5f4d4cf3c..9f07ad1624 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d47a7207e83fe770d4925b91b4bb0eee6cb2c494c46b1f3d5d6d0720464392d6 -size 44992 +oid sha256:3fced9019f81e5804ea57c0b1a2750868626ad35b9a7227009a796d289595556 +size 44659 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png index 54890aa7bd..74c4a9eef1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:605ea373238bbaa4d3820adf13bf3896d9a5c5b6ceb6c022a7f3b8ae817f921f -size 43927 +oid sha256:be9bb839a503d169d67d4332a133ce2b3d76b3ff4c52c063436521f8a74c973e +size 43894 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png index 385ca39bc9..bd85882fde 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4dab72328925eb45cd529913972670587ea4257d2ff7affd708295d68e783d7 -size 45579 +oid sha256:82c2781c37b7cefa198f2061a2c8e900b27bd57653db88733be5ff55dfaf5eee +size 45251 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png index 064cfc5b27..cac488f3b8 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76cc029a07c0fb568d9e15864be667478cd9927095f66ec2f590a1466c58a291 -size 45497 +oid sha256:36feab599a6a2d4269b68f42149a6f82832ac0f6f0e9f01fb49adc21b3a58d93 +size 45172 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png index 3bcd7d1de4..3e7cfcf672 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42a8b9b5abe3a5a3fe61e27924ca75ea7925e857005d5de3cd3ceb81500f73c2 -size 45981 +oid sha256:04bedd62b5ceba766197dd18da9efb0ff086c2c01c9c19446f8a3c42ed43ad87 +size 46813 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png index 5dc96aaa2c..27ec9f7a25 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19cc2df1da3b685668afe309bd305d5ac8447d0997c7ad902685d44daa26239f -size 46556 +oid sha256:828b065aaeeccd826e98e9601069819d7a75ffabab9820a834cacb627416edc0 +size 47409 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png index 2b4718d729..f96e1aec74 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5625bb01c24d969adeecdb536e145ba549ef1c10d99e16f7a68c3bd577fbefbc -size 45804 +oid sha256:4740bcd118f8583334dd1c03b5011e33c5384ea6810d1c83711f538cf9f1d165 +size 45521 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png index 4f40cab65a..4d88149a93 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bdd0b00fddd9fc62b5c9c97b5ff766591c0a1822730bd249059ed02508a65ee -size 45353 +oid sha256:7fab53a63021ddae8384aed8eaf0a3803d06e8c80645ecc13f79a7f5cced6e68 +size 45024 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png index 3151e8d088..fa7e99aef2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fb58a8db96c0e76d5a0e8c8d2a38ae206e3dade479bd924fd404902c8e69b42 -size 41322 +oid sha256:89a2c619412ad9ca43c79cb76ebe72b827da71743dda7044a9b44de2990f160b +size 42388 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png index e213e801f3..9b27b61083 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5030731135dec08034e1992499caa251aae8039cebafb09210fc83d622b06bd9 -size 41257 +oid sha256:56ceed440bb51039ffe7e4419477a57a235eed153a0188838e6265c241896a82 +size 42262 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png index 02be5d5da0..0542085126 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4dd437c614c904bf210e5b51fe017f26cf849e7572b09966e834ac6fd429d347 -size 41380 +oid sha256:3b03e9da2d73cc0e3d0485b73470e576d7ebd4b8b5f375f2a478f1c1524772fa +size 39948 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png index 7c037817b4..a320657a26 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:380fbdd2caa485b9fe84a331e64139ccb23998ce1d95158c6345364daa3810b8 -size 46048 +oid sha256:a686a2596b9b85b17c6882aa986b12e13a83a47ed217c4eb6de6aa2cb3ec2d50 +size 45717 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png index 7b74e5750c..920b670806 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4610f8488c8c8edf05e2cd3e09470660b2c4db14650907c28367d2b38bce852 -size 45792 +oid sha256:d6bda369c1754e2c07216f4bff0285e017f87b550fe0dc6bb5f127845d52c9b6 +size 45462 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png index a2e1108c0c..aeaa04b269 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efb795d2808d7ae5e38eda42105101dce377154c794844f752e4cb80dcabd681 -size 45462 +oid sha256:285045769d912e98df0371c829affd925642c8a20cd0b6b09dfa92ab5f143538 +size 45131 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png index 2d38f9d8b1..e7fe0e7cef 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dca0800e7e68e65bd27814b4c86123da40385d6e85153e003ba58ca7689b5f8 -size 38742 +oid sha256:463548e170b48509d68c0993acb000748b19921e5d4be784b07ce83252c8a5bb +size 38613 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png index 1fc42278e2..76eb564d8c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:351183a1b0fb44dc42c790a625ee6aa5740b27fa95b138252b72bf67268f5a16 -size 43015 +oid sha256:c1d19c2b8c6b3c80b68214da2bb9f54a6897a279aa70e2d8cab88c291332cece +size 42980 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png index da0c4a9dde..40ab9e310a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31f92cc4d97c7129bc37da51e6f0a10612d4f61803762b1709c5d72f1cda86f1 -size 43337 +oid sha256:2057479cad5d3b9a89ec835ee1d06705c6c459f7810c2875bea26b975944bcf7 +size 42960 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png index 5c16501c76..e549a92821 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e54b946de1128b1109ece7553e73d7676199edc45e7f275014c37503336d302e -size 40862 +oid sha256:073addf26e8d0b638bce93db99d1fe9740abd89613a1eb8ef5bb4324e489d6fa +size 41924 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png index 55ab231ede..29a8b0b4b1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f3df6d78498d2b390cf926fb1111fdfa0f38ba5dd9e023b9bd2af42c3951511 -size 41921 +oid sha256:56d72cab4a9d7af568cc497b68d6ca9055874544cfd462c93da37fe368801fdf +size 42984 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png index ae9d249cb2..91cf700f52 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e15030fc182998a37493228d91c888d6b499b6a684b1a4e15d402c9156a40ba -size 46678 +oid sha256:9141f5fa4e054dc5cf70126309830bc38d564d467a215eaa1130f3601b15e761 +size 46350 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png index 214dee1111..fe0b629925 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b773cce165a6410140afbf0daf3ec526233b97ea707811a3794e591704237398 -size 45536 +oid sha256:a062b8cbe4dfbd94a5b068b1f7c4654ed8b697a7e98ae4c2bcb6c3e5bf473b49 +size 45289 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png index 41946442cb..26d59539db 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f28bec4fb21e827f3467bc08ab0f33e917cf76360903b6e7df6282a7589cae7e -size 45545 +oid sha256:46aafd39e1a93f8d357def4c5f3f024ad0ee13931ebdb9f72475dbc911fd7532 +size 45266 From 67c668990e3f798d980685e12804fe68ca462f11 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 14:50:26 +0200 Subject: [PATCH 205/471] Hide edit pencil from accessibility Closes #6378 --- .../libraries/matrix/ui/components/AvatarPickerView.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt index e743a2447c..4d3ba4dc6b 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.hideFromAccessibility import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp @@ -196,7 +197,10 @@ private fun BoxScope.OverlayEditButton( .clip(CircleShape) .clickable(interactionSource = interactionSource, onClick = onClick, indication = null) .background(ElementTheme.colors.bgCanvasDefault) - .border(BorderStroke(1.dp, ElementTheme.colors.borderInteractiveSecondary), shape = CircleShape), + .border(BorderStroke(1.dp, ElementTheme.colors.borderInteractiveSecondary), shape = CircleShape) + .clearAndSetSemantics { + hideFromAccessibility() + }, contentAlignment = Alignment.Center, ) { Icon( From 42566b4b1f28cd737cdbd0fceef5d15f27a703b7 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 20 May 2026 15:23:03 +0200 Subject: [PATCH 206/471] Release proximity wakelock on Element Call when call ends (#6825) The `CoroutineScope` that launched this logic was cancelled by that point, so the wakelock was never released. --- .../features/call/impl/utils/WebViewAudioManager.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt index 2d674d21af..febd919149 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/WebViewAudioManager.kt @@ -201,12 +201,9 @@ class WebViewAudioManager( return } - coroutineScope.launch { - proximitySensorMutex.withLock { - if (proximitySensorWakeLock?.isHeld == true) { - proximitySensorWakeLock?.release() - } - } + // Since this should run when the call is no longer running, it should be OK to not use the mutex here + if (proximitySensorWakeLock?.isHeld == true) { + proximitySensorWakeLock?.release() } audioManager.mode = AudioManager.MODE_NORMAL From 620f1865e8db8fc46a005aa1fb822d7e7fb6ab7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 15:39:11 +0200 Subject: [PATCH 207/471] [a11y] Improve accessibility of screen headers. --- .../impl/threads/list/ThreadsListView.kt | 16 ++++++++++++- .../impl/topbars/MessagesViewTopBar.kt | 8 +++---- .../messages/impl/topbars/ThreadTopBar.kt | 23 +++++++++++++------ .../src/main/res/values/localazy.xml | 2 ++ 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt index 1beabcd3fa..5e26d849a5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt @@ -31,6 +31,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.heading import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -79,7 +82,18 @@ fun ThreadsListView( topBar = { TopAppBar( title = { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { + val description = stringResource( + CommonStrings.a11y_threads_in_room, + state.roomName, + ) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clearAndSetSemantics { + heading() + contentDescription = description + }, + ) { Avatar( avatarData = AvatarData( id = state.roomId.value, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt index 639092fc6c..4f023ad2bd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/MessagesViewTopBar.kt @@ -80,7 +80,8 @@ internal fun MessagesViewTopBar( Row( modifier = Modifier .clip(roundedCornerShape) - .clickable { onRoomDetailsClick() }, + .clickable { onRoomDetailsClick() } + .semantics { heading() }, horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, ) { @@ -158,10 +159,7 @@ private fun RoomAvatarAndNameRow( ) Text( modifier = Modifier - .padding(start = 8.dp) - .semantics { - heading() - }, + .padding(start = 8.dp), text = roomName ?: stringResource(CommonStrings.common_no_room_name), style = ElementTheme.typography.fontBodyLgMedium, fontStyle = FontStyle.Italic.takeIf { roomName == null }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt index e73b6b19b7..5ef4541f06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/ThreadTopBar.kt @@ -17,8 +17,9 @@ 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.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading -import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -58,7 +59,18 @@ internal fun ThreadTopBar( BackButton(onClick = onBackClick) }, title = { - Row(verticalAlignment = Alignment.CenterVertically) { + val name = roomName ?: stringResource(CommonStrings.common_no_room_name) + val description = stringResource( + CommonStrings.a11y_thread_in_room, + name, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clearAndSetSemantics { + heading() + contentDescription = description + }, + ) { Avatar( avatarData = roomAvatarData, avatarType = AvatarType.Room( @@ -69,17 +81,14 @@ internal fun ThreadTopBar( Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 8.dp) - .semantics { - heading() - }, + .padding(horizontal = 8.dp), ) { Text( text = stringResource(CommonStrings.common_thread), style = ElementTheme.typography.fontBodyLgMedium, ) Text( - text = roomName ?: stringResource(CommonStrings.common_no_room_name), + text = name, style = ElementTheme.typography.fontBodySmRegular, fontStyle = FontStyle.Italic.takeIf { roomName == null }, color = ElementTheme.colors.textSecondary, diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ee0f51000a..6fa5436f96 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -54,6 +54,8 @@ "Start a call" "Start a video call" "Start a voice call" + "Thread in %1$s" + "Threads in %1$s" "Tombstoned room" "User avatar" "User menu" From 5b2970f78906cb22312dcbcc0c37036c114804a8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 15:49:18 +0200 Subject: [PATCH 208/471] [a11y] Improve accessibility of screen headers. --- .../mediaviewer/impl/viewer/MediaViewerView.kt | 14 +++++++++++--- .../ui-strings/src/main/res/values/localazy.xml | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index d52db124ea..3a7c006593 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -55,6 +55,8 @@ import androidx.compose.ui.layout.onVisibilityChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow @@ -495,14 +497,20 @@ private fun MediaViewerTopBar( TopAppBar( title = { if (senderName != null && dateSent != null) { + val description = stringResource( + CommonStrings.a11y_sent_by_sender_at_date, + senderName, + dateSent, + ) Column( modifier = Modifier .fillMaxWidth() + .clearAndSetSemantics { + heading() + contentDescription = description + }, ) { Text( - modifier = Modifier.semantics { - heading() - }, text = senderName, style = ElementTheme.typography.fontBodyMdMedium, color = ElementTheme.colors.textPrimary, diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 6fa5436f96..1c94927623 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -48,6 +48,7 @@ "Room avatar" "Send files" "Sender location" + "Sent by %1$s at %2$s" "Time limited action required, you have one minute to verify" "Settings, action required" "Show password" From c2e357cbcdc4aa519cbc38690da7c860cafeaf94 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 15:52:46 +0200 Subject: [PATCH 209/471] [a11y] Improve accessibility of screen headers. --- .../android/features/space/impl/root/SpaceView.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt index 05fb75ee5e..29898a1f63 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceView.kt @@ -354,7 +354,8 @@ private fun EmptySpaceView( title = stringResource(R.string.screen_space_empty_state_title), subTitle = null, iconStyle = BigIcon.Style.Default(vectorIcon = CompoundIcons.Room(), usePrimaryTint = true), - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .padding(top = 40.dp, start = 24.dp, end = 24.dp, bottom = 24.dp), ) ButtonColumnMolecule( @@ -425,6 +426,7 @@ private fun SpaceViewTopBar( modifier = Modifier .clip(roundedCornerShape) .clickable(enabled = canAccessSpaceSettings, onClick = onSettingsClick) + .semantics { heading() } ) }, actions = { @@ -532,6 +534,7 @@ private fun ManageModeTopBar( Text( text = pluralStringResource(CommonPlurals.common_selected_count, selectedCount, selectedCount), style = ElementTheme.typography.fontBodyLgMedium, + modifier = Modifier.semantics { heading() }, ) }, actions = { @@ -585,10 +588,7 @@ private fun SpaceAvatarAndNameRow( ) Text( modifier = Modifier - .padding(horizontal = 8.dp) - .semantics { - heading() - }, + .padding(horizontal = 8.dp), text = name ?: stringResource(CommonStrings.common_no_space_name), style = ElementTheme.typography.fontBodyLgMedium, fontStyle = FontStyle.Italic.takeIf { name == null }, From df9a3fe9c2b2aee793618b92c4f24fc43fb59af9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 15:53:08 +0200 Subject: [PATCH 210/471] [a11y] Improve accessibility of screen headers. --- .../element/android/features/joinroom/impl/JoinRoomView.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt index 35cfbb1594..977308178d 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomView.kt @@ -609,6 +609,7 @@ private fun JoinRoomTopBar( val roundedCornerShape = RoundedCornerShape(8.dp) val titleModifier = Modifier .clip(roundedCornerShape) + .semantics { heading() } if (contentState.name != null) { Row( modifier = titleModifier, @@ -621,10 +622,7 @@ private fun JoinRoomTopBar( ) Text( modifier = Modifier - .padding(horizontal = 8.dp) - .semantics { - heading() - }, + .padding(horizontal = 8.dp), text = contentState.name, style = ElementTheme.typography.fontBodyLgMedium, maxLines = 1, From bd01b275174c2931729478ccaefe6c672e0996cf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 16:21:06 +0200 Subject: [PATCH 211/471] [a11y] Ensure that video overlay with controls is never hidden when screen reader is enabled. --- libraries/mediaviewer/impl/build.gradle.kts | 1 + .../impl/local/video/MediaVideoView.kt | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index f2dbf1aacf..b723578829 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixmedia.api) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.uiUtils) implementation(projects.libraries.voiceplayer.api) implementation(projects.services.toolbox.api) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt index 9d5e290857..d89762b72e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt @@ -57,6 +57,7 @@ import io.element.android.libraries.mediaviewer.impl.local.player.rememberExoPla import io.element.android.libraries.mediaviewer.impl.local.player.seekToEnsurePlaying import io.element.android.libraries.mediaviewer.impl.local.player.togglePlay import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState +import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import kotlinx.coroutines.delay import me.saket.telephoto.zoomable.zoomable import timber.log.Timber @@ -162,12 +163,20 @@ private fun ExoPlayerMediaVideoView( var autoHideController by remember { mutableIntStateOf(0) } - LaunchedEffect(autoHideController) { - delay(5.seconds) - if (exoPlayer.isPlaying) { + val isTalkbackActive = isTalkbackActive() + LaunchedEffect(autoHideController, isTalkbackActive) { + if (isTalkbackActive) { + // Ensure that the controller is always visible when talkback is active mediaPlayerControllerState = mediaPlayerControllerState.copy( - isVisible = false, + isVisible = true, ) + } else { + delay(5.seconds) + if (exoPlayer.isPlaying) { + mediaPlayerControllerState = mediaPlayerControllerState.copy( + isVisible = false, + ) + } } } From 4641ae666ef0d870b0621d95c310636b10e458f6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 17:04:24 +0200 Subject: [PATCH 212/471] [a11y] Improve accessibility of media controller --- .../local/player/MediaPlayerControllerView.kt | 39 +++++++++++++++---- .../src/main/res/values/localazy.xml | 2 + 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index b06b97f491..8d94ba9d2e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -28,6 +28,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -100,36 +103,54 @@ fun MediaPlayerControllerView( contentColor = ElementTheme.colors.iconOnSolidPrimary, ) } + val a11yPause = stringResource(CommonStrings.a11y_pause) + val a11yPlay = stringResource(CommonStrings.a11y_play) IconButton( modifier = Modifier - .size(36.dp), + .size(36.dp) + .semantics { + stateDescription = if (state.isPlaying) a11yPause else a11yPlay + }, onClick = onTogglePlay, colors = colors, ) { if (state.isPlaying) { Icon( imageVector = CompoundIcons.PauseSolid(), - contentDescription = stringResource(CommonStrings.a11y_pause) + contentDescription = null, ) } else { Icon( imageVector = CompoundIcons.PlaySolid(), - contentDescription = stringResource(CommonStrings.a11y_play) + contentDescription = null, ) } } + val position = state.displayProgressInMillis.toHumanReadableDuration() + val a11yPosition = stringResource(CommonStrings.a11y_position, position) Text( modifier = Modifier .widthIn(min = 48.dp) - .padding(horizontal = 8.dp), - text = state.displayProgressInMillis.toHumanReadableDuration(), + .padding(horizontal = 8.dp) + .semantics { + contentDescription = a11yPosition + }, + text = position, textAlign = TextAlign.Center, color = ElementTheme.colors.textPrimary, style = ElementTheme.typography.fontBodyXsMedium, ) var lastSelectedValue by remember { mutableFloatStateOf(-1f) } Slider( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .semantics { + // Speak out a progress percent instead of milliseconds + stateDescription = buildString { + append((state.progressAsFloat * 100).toInt()) + append("%") + } + }, valueRange = 0f..state.durationInMillis.toFloat(), value = lastSelectedValue.takeIf { it >= 0 } ?: state.seekingToMillis?.toFloat() @@ -146,10 +167,14 @@ fun MediaPlayerControllerView( val formattedDuration = remember(state.durationInMillis) { state.durationInMillis.toHumanReadableDuration() } + val a11yDuration = stringResource(CommonStrings.a11y_duration, formattedDuration) Text( modifier = Modifier .widthIn(min = 48.dp) - .padding(horizontal = 8.dp), + .padding(horizontal = 8.dp) + .semantics { + contentDescription = a11yDuration + }, text = formattedDuration, textAlign = TextAlign.Center, color = ElementTheme.colors.textPrimary, diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ee0f51000a..fd3679a140 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -9,6 +9,7 @@ "%1$d digit entered" "%1$d digits entered" + "Duration: %1$s" "Edit avatar" "The full address will be %1$s" "Encryption details" @@ -33,6 +34,7 @@ "Playback speed" "Poll" "Ended poll" + "Position: %1$s" "QR Code" "React with %1$s" "React with other emojis" From a33d717aa0965e8ed9f1ff2c85f4998329914c7f Mon Sep 17 00:00:00 2001 From: cizra Date: Wed, 20 May 2026 15:19:08 +0000 Subject: [PATCH 213/471] Don't compress images sent through the Files attachment picker (#6755) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't compress images sent through the Files attachment picker Images and videos picked through the "Attachment" picker are now uploaded without re-encoding, regardless of the "Optimize media quality" setting. The gallery and camera pickers keep the existing behaviour, matching what Element Web/Desktop and most other messengers do. Fixes #6365 Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Make sure we select the right video preset for sending as file Wait for the video size estimations to be calculated before preprocessing the video file --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jorge Martín --- .../messages/impl/attachments/Attachment.kt | 9 +- .../preview/AttachmentsPreviewPresenter.kt | 49 +++++++++- ...faultMediaOptimizationSelectorPresenter.kt | 38 ++++---- .../MediaOptimizationSelectorPresenter.kt | 1 + .../video/VideoCompressionPresetSelector.kt | 31 +++++++ .../MessageComposerPresenter.kt | 5 +- .../AttachmentsPreviewPresenterTest.kt | 83 ++++++++++++++++- ...tMediaOptimizationSelectorPresenterTest.kt | 69 ++++++++++++++ .../VideoCompressionPresetSelectorTest.kt | 92 +++++++++++++++++++ .../impl/fixtures/MediaAttachmentFixtures.kt | 3 +- ...diaOptimizationSelectorPresenterFactory.kt | 2 +- .../mediaupload/test/FakeMediaPreProcessor.kt | 5 + 12 files changed, 357 insertions(+), 30 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoCompressionPresetSelector.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/VideoCompressionPresetSelectorTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt index d989b34ab3..73fa55228d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/Attachment.kt @@ -16,5 +16,12 @@ import kotlinx.parcelize.Parcelize @Immutable sealed interface Attachment : Parcelable { @Parcelize - data class Media(val localMedia: LocalMedia) : Attachment + data class Media( + val localMedia: LocalMedia, + // When true, the media was picked through the "Files" picker and should be + // uploaded without image recompression; videos still use the highest available + // / best-fit preset rather than an additional size-reduction optimization pass. + // See https://github.com/element-hq/element-x-android/issues/6365 + val sendAsFile: Boolean = false, + ) : Attachment } 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 8e92e53f6a..fc7f3034a6 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 @@ -23,6 +23,8 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorPresenter +import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState +import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.androidutils.hash.hash @@ -61,6 +63,7 @@ class AttachmentsPreviewPresenter( private val permalinkBuilder: PermalinkBuilder, private val temporaryUriDeleter: TemporaryUriDeleter, private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory, + private val videoCompressionPresetSelector: VideoCompressionPresetSelector, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val dispatchers: CoroutineDispatchers, private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider, @@ -96,7 +99,10 @@ class AttachmentsPreviewPresenter( val mediaAttachment = attachment as Attachment.Media val mediaOptimizationSelectorPresenter = remember { - mediaOptimizationSelectorPresenterFactory.create(mediaAttachment.localMedia) + mediaOptimizationSelectorPresenterFactory.create( + localMedia = mediaAttachment.localMedia, + sendAsFile = mediaAttachment.sendAsFile, + ) } val mediaOptimizationSelectorState by rememberUpdatedState(mediaOptimizationSelectorPresenter.present()) @@ -104,14 +110,25 @@ class AttachmentsPreviewPresenter( var displayFileTooLargeError by remember { mutableStateOf(false) } - LaunchedEffect(mediaOptimizationSelectorState.displayMediaSelectorViews) { + LaunchedEffect( + mediaOptimizationSelectorState.displayMediaSelectorViews, + mediaOptimizationSelectorState.videoSizeEstimations, + ) { // If the media optimization selector is not displayed, we can pre-process the media // to prepare it for sending. This is done to avoid blocking the UI thread when the // user clicks on the send button. - if (mediaOptimizationSelectorState.displayMediaSelectorViews == false) { - preprocessMediaJob = preProcessAttachment( + if (mediaOptimizationSelectorState.displayMediaSelectorViews == false && preprocessMediaJob == null) { + if (mediaAttachment.localMedia.info.mimeType.isMimeTypeVideo() && mediaOptimizationSelectorState.videoSizeEstimations.dataOrNull() == null) { + Timber.d("Waiting for video size estimations to be able to select the best video compression preset before pre-processing the media") + return@LaunchedEffect + } + val config = getAutoPreprocessMediaOptimizationConfig( + mediaAttachment = mediaAttachment, + mediaOptimizationSelectorState = mediaOptimizationSelectorState, + ) ?: return@LaunchedEffect + preprocessMediaJob = coroutineScope.preProcessAttachment( attachment = attachment, - mediaOptimizationConfig = mediaOptimizationConfigProvider.get(), + mediaOptimizationConfig = config, displayProgress = false, sendActionState = sendActionState, ) @@ -233,6 +250,28 @@ class AttachmentsPreviewPresenter( ) } + private suspend fun getAutoPreprocessMediaOptimizationConfig( + mediaAttachment: Attachment.Media, + mediaOptimizationSelectorState: MediaOptimizationSelectorState, + ): MediaOptimizationConfig? { + return if (mediaAttachment.sendAsFile) { + // If we're sending the media as a file, we can skip image compression and we should select the highest video compression preset that still fits + // the upload limit (if the estimations are available) + val videoCompressionPreset = videoCompressionPresetSelector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = mediaOptimizationSelectorState.videoSizeEstimations, + ).dataOrNull() ?: VideoCompressionPreset.HIGH + + MediaOptimizationConfig( + compressImages = false, + videoCompressionPreset = videoCompressionPreset, + ) + } else { + // Otherwise, we just rely on the user preferences for media optimization + mediaOptimizationConfigProvider.get() + } + } + private fun CoroutineScope.preProcessAttachment( attachment: Attachment, mediaOptimizationConfig: MediaOptimizationConfig, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt index c81c306f90..abc0264b2f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt @@ -37,9 +37,11 @@ import kotlin.math.roundToLong @AssistedInject class DefaultMediaOptimizationSelectorPresenter( @Assisted private val localMedia: LocalMedia, + @Assisted private val sendAsFile: Boolean, private val maxUploadSizeProvider: MaxUploadSizeProvider, private val featureFlagService: FeatureFlagService, private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider, + private val videoCompressionPresetSelector: VideoCompressionPresetSelector, mediaExtractorFactory: VideoMetadataExtractor.Factory, ) : MediaOptimizationSelectorPresenter { @ContributesBinding(SessionScope::class) @@ -47,6 +49,7 @@ class DefaultMediaOptimizationSelectorPresenter( interface Factory : MediaOptimizationSelectorPresenter.Factory { override fun create( localMedia: LocalMedia, + sendAsFile: Boolean, ): DefaultMediaOptimizationSelectorPresenter } @@ -55,7 +58,9 @@ class DefaultMediaOptimizationSelectorPresenter( @Composable override fun present(): MediaOptimizationSelectorState { val displayMediaSelectorViews by produceState(null) { - value = featureFlagService.isFeatureEnabled(FeatureFlags.SelectableMediaQuality) + // When sending as a raw file, never show the optimization selector: images skip + // recompression, while videos use the highest available best-fit preset. + value = !sendAsFile && featureFlagService.isFeatureEnabled(FeatureFlags.SelectableMediaQuality) } var displayVideoPresetSelectorDialog by remember { mutableStateOf(false) } @@ -123,12 +128,23 @@ class DefaultMediaOptimizationSelectorPresenter( var selectedVideoOptimizationPreset by remember { mutableStateOf>(AsyncData.Loading()) } LaunchedEffect(videoSizeEstimations.dataOrNull()) { + if (sendAsFile) { + // Send-as-file path: pin to no image compression, and pick the highest-quality + // video preset that still fits the upload limit (we have no true "do not re-encode + // video" path in the pre-processor right now). + selectedImageOptimization = AsyncData.Success(false) + selectedVideoOptimizationPreset = videoCompressionPresetSelector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = videoSizeEstimations, + ) + return@LaunchedEffect + } val mediaOptimizationConfig = mediaOptimizationConfigProvider.get() selectedImageOptimization = AsyncData.Success(mediaOptimizationConfig.compressImages) // Find the best video preset based on the default preset and the video size estimations // Since the estimation for the current preset may be way too large to upload, we check the ones that provide lower file sizes - selectedVideoOptimizationPreset = findBestVideoPreset( - defaultVideoPreset = mediaOptimizationConfig.videoCompressionPreset, + selectedVideoOptimizationPreset = videoCompressionPresetSelector.selectBestVideoPreset( + expectedVideoPreset = mediaOptimizationConfig.videoCompressionPreset, videoSizeEstimations = videoSizeEstimations, ) } @@ -176,20 +192,4 @@ class DefaultMediaOptimizationSelectorPresenter( eventSink = ::handleEvent, ) } - - private fun findBestVideoPreset( - defaultVideoPreset: VideoCompressionPreset, - videoSizeEstimations: AsyncData>, - ): AsyncData { - val estimations = videoSizeEstimations.dataOrNull() ?: return AsyncData.Loading() - // This will find the best video preset that can be used to produce a video that can be uploaded - val bestEstimation = estimations.find { it.preset.ordinal >= defaultVideoPreset.ordinal && it.canUpload }?.preset - return if (bestEstimation != null) { - AsyncData.Success(bestEstimation) - } else { - AsyncData.Failure( - IllegalStateException("No suitable video preset found for default preset: $defaultVideoPreset") - ) - } - } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt index 80cdfd9467..f1e17ef0a6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/MediaOptimizationSelectorPresenter.kt @@ -15,6 +15,7 @@ fun interface MediaOptimizationSelectorPresenter : Presenter>, + ): AsyncData { + val estimations = videoSizeEstimations.dataOrNull() ?: return AsyncData.Loading() + val bestEstimation = estimations.find { it.preset.ordinal >= expectedVideoPreset.ordinal && it.canUpload }?.preset + return if (bestEstimation != null) { + AsyncData.Success(bestEstimation) + } else { + AsyncData.Failure( + IllegalStateException("No suitable video preset found for expected preset: $expectedVideoPreset") + ) + } + } +} 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 b42cda7dfc..e226318345 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 @@ -179,7 +179,7 @@ class MessageComposerPresenter( handlePickedMedia(uri, mimeType) } val filesPicker = mediaPickerProvider.registerFilePicker(AnyMimeTypes) { uri, mimeType -> - handlePickedMedia(uri, mimeType ?: MimeTypes.OctetStream) + handlePickedMedia(uri, mimeType ?: MimeTypes.OctetStream, sendAsFile = true) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker { uri -> handlePickedMedia(uri, MimeTypes.Jpeg) @@ -605,6 +605,7 @@ class MessageComposerPresenter( private fun handlePickedMedia( uri: Uri?, mimeType: String? = null, + sendAsFile: Boolean = false, ) { uri ?: return val localMedia = localMediaFactory.createFromUri( @@ -613,7 +614,7 @@ class MessageComposerPresenter( name = null, formattedFileSize = null ) - val mediaAttachment = Attachment.Media(localMedia) + val mediaAttachment = Attachment.Media(localMedia, sendAsFile = sendAsFile) val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId navigator.navigateToPreviewAttachments(persistentListOf(mediaAttachment), inReplyToEventId) 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 384b78471d..9a9dc08834 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 @@ -17,6 +17,7 @@ import io.element.android.features.messages.impl.attachments.preview.Attachments 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.attachments.video.MediaOptimizationSelectorState +import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation import io.element.android.features.messages.impl.fixtures.aMediaAttachment import io.element.android.features.messages.test.attachments.video.FakeMediaOptimizationSelectorPresenterFactory @@ -45,6 +46,7 @@ import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfig import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.mediaviewer.api.aVideoMediaInfo import io.element.android.libraries.mediaviewer.api.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia import io.element.android.libraries.preferences.api.store.VideoCompressionPreset @@ -548,10 +550,87 @@ class AttachmentsPreviewPresenterTest { } } + @Test + fun `present - sendAsFile attachment is pre-processed without image compression`() = runTest { + // Even though the user has enabled "Optimize media quality" globally, picking the file + // through the Files picker (sendAsFile = true) must skip compression. Regression test + // for https://github.com/element-hq/element-x-android/issues/6365 + val mediaPreProcessor = FakeMediaPreProcessor() + val presenter = createAttachmentsPreviewPresenter( + localMedia = aLocalMedia(mockMediaUrl, anImageMediaInfo()), + sendAsFile = true, + mediaPreProcessor = mediaPreProcessor, + // Selector views are hidden in the sendAsFile flow, which triggers the auto pre-process path. + displayMediaQualitySelectorViews = false, + mediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider( + config = MediaOptimizationConfig( + compressImages = true, + videoCompressionPreset = VideoCompressionPreset.STANDARD, + ) + ), + ) + + presenter.test { + consumeItemsUntilPredicate { mediaPreProcessor.processCallCount > 0 } + assertThat(mediaPreProcessor.lastMediaOptimizationConfig).isEqualTo( + MediaOptimizationConfig( + compressImages = false, + videoCompressionPreset = VideoCompressionPreset.HIGH, + ) + ) + } + } + + @Test + fun `present - sendAsFile video is pre-processed with best fitting preset`() = runTest { + val mediaPreProcessor = FakeMediaPreProcessor() + val presenter = createAttachmentsPreviewPresenter( + localMedia = aLocalMedia(mockMediaUrl, aVideoMediaInfo()), + sendAsFile = true, + mediaPreProcessor = mediaPreProcessor, + // Selector views are hidden in the sendAsFile flow, which triggers the auto pre-process path. + displayMediaQualitySelectorViews = false, + mediaOptimizationSelectorPresenterFactory = FakeMediaOptimizationSelectorPresenterFactory { + MediaOptimizationSelectorState( + maxUploadSize = AsyncData.Success(250_000_000L), + videoSizeEstimations = AsyncData.Success( + persistentListOf( + VideoUploadEstimation(VideoCompressionPreset.HIGH, sizeInBytes = 513_216_000L, canUpload = false), + VideoUploadEstimation(VideoCompressionPreset.STANDARD, sizeInBytes = 228_096_000L, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.LOW, sizeInBytes = 57_024_000L, canUpload = true), + ) + ), + isImageOptimizationEnabled = false, + selectedVideoPreset = VideoCompressionPreset.STANDARD, + displayMediaSelectorViews = false, + displayVideoPresetSelectorDialog = false, + eventSink = {}, + ) + }, + mediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider( + config = MediaOptimizationConfig( + compressImages = true, + videoCompressionPreset = VideoCompressionPreset.LOW, + ) + ), + ) + + presenter.test { + consumeItemsUntilPredicate { mediaPreProcessor.processCallCount > 0 } + assertThat(mediaPreProcessor.lastMediaOptimizationConfig).isEqualTo( + MediaOptimizationConfig( + compressImages = false, + videoCompressionPreset = VideoCompressionPreset.STANDARD, + ) + ) + } + } + private fun TestScope.createAttachmentsPreviewPresenter( localMedia: LocalMedia = aLocalMedia( uri = mockMediaUrl, ), + sendAsFile: Boolean = false, room: JoinedRoom = FakeJoinedRoom(), timelineMode: Timeline.Mode = Timeline.Mode.Live, permalinkBuilder: PermalinkBuilder = FakePermalinkBuilder(), @@ -573,9 +652,10 @@ class AttachmentsPreviewPresenterTest { } ), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), + videoCompressionPresetSelector: VideoCompressionPresetSelector = VideoCompressionPresetSelector(), ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( - attachment = aMediaAttachment(localMedia), + attachment = aMediaAttachment(localMedia, sendAsFile = sendAsFile), onDoneListener = onDoneListener, mediaSenderFactory = MediaSenderFactory { timelineMode -> DefaultMediaSender( @@ -592,6 +672,7 @@ class AttachmentsPreviewPresenterTest { sessionCoroutineScope = this, dispatchers = testCoroutineDispatchers(), mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory, + videoCompressionPresetSelector = videoCompressionPresetSelector, timelineMode = timelineMode, inReplyToEventId = null, mediaOptimizationConfigProvider = mediaOptimizationConfigProvider, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt index 106fff7375..e06f4ad4cd 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenterTest.kt @@ -210,19 +210,88 @@ class DefaultMediaOptimizationSelectorPresenterTest { } } + @Test + fun `present - sendAsFile hides selector views and disables image compression for images`() = runTest { + val presenter = createDefaultMediaOptimizationSelectorPresenter( + localMedia = aLocalMedia(mockMediaUrl, anImageMediaInfo()), + // Even with the feature flag on, sendAsFile must hide the selector. + featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SelectableMediaQuality.key to true)), + // And it must override the user's "optimize images" preference. + mediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), + sendAsFile = true, + ) + presenter.test { + // Initial loading state + skipItems(1) + awaitItem().run { + assertThat(displayMediaSelectorViews).isFalse() + assertThat(isImageOptimizationEnabled).isFalse() + } + } + } + + @Test + fun `present - sendAsFile picks HIGH video preset when the video fits the upload limit`() = runTest { + val presenter = createDefaultMediaOptimizationSelectorPresenter( + // Plenty of room: even HIGH preset will fit. + maxUploadSizeProvider = MaxUploadSizeProvider { Result.success(Long.MAX_VALUE) }, + mediaExtractorFactory = FakeVideoMetadataExtractorFactory( + FakeVideoMetadataExtractor( + sizeResult = Result.success(Size(1920, 1080)), + duration = Result.success(10.minutes) + ) + ), + sendAsFile = true, + ) + presenter.test { + // Initial loading state, then the one with size estimations loaded. + skipItems(1) + awaitItem().run { + assertThat(displayMediaSelectorViews).isFalse() + assertThat(selectedVideoPreset).isEqualTo(VideoCompressionPreset.HIGH) + } + } + } + + @Test + fun `present - sendAsFile picks lower video preset when HIGH exceeds the upload limit`() = runTest { + val presenter = createDefaultMediaOptimizationSelectorPresenter( + maxUploadSizeProvider = MaxUploadSizeProvider { Result.success(250_000_000L) }, + mediaExtractorFactory = FakeVideoMetadataExtractorFactory( + FakeVideoMetadataExtractor( + sizeResult = Result.success(Size(1920, 1080)), + duration = Result.success(10.minutes) + ) + ), + sendAsFile = true, + ) + presenter.test { + // Initial loading state, then the one with size estimations loaded. + skipItems(1) + awaitItem().run { + assertThat(displayMediaSelectorViews).isFalse() + assertThat(selectedVideoPreset).isEqualTo(VideoCompressionPreset.STANDARD) + } + } + } + private fun createDefaultMediaOptimizationSelectorPresenter( localMedia: LocalMedia = aLocalMedia(mockMediaUrl, aVideoMediaInfo()), maxUploadSizeProvider: MaxUploadSizeProvider = MaxUploadSizeProvider { Result.success(1_000L) }, featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SelectableMediaQuality.key to true)), mediaExtractorFactory: FakeVideoMetadataExtractorFactory = FakeVideoMetadataExtractorFactory(), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), + videoCompressionPresetSelector: VideoCompressionPresetSelector = VideoCompressionPresetSelector(), + sendAsFile: Boolean = false, ): DefaultMediaOptimizationSelectorPresenter { return DefaultMediaOptimizationSelectorPresenter( localMedia = localMedia, + sendAsFile = sendAsFile, maxUploadSizeProvider = maxUploadSizeProvider, featureFlagService = featureFlagService, mediaExtractorFactory = mediaExtractorFactory, mediaOptimizationConfigProvider = mediaOptimizationConfigProvider, + videoCompressionPresetSelector = videoCompressionPresetSelector, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/VideoCompressionPresetSelectorTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/VideoCompressionPresetSelectorTest.kt new file mode 100644 index 0000000000..d3864794c2 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/video/VideoCompressionPresetSelectorTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.video + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.preferences.api.store.VideoCompressionPreset +import kotlinx.collections.immutable.persistentListOf +import org.junit.Test + +class VideoCompressionPresetSelectorTest { + private val selector = VideoCompressionPresetSelector() + + @Test + fun `selectBestVideoPreset - returns expected preset when it can upload`() { + val result = selector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = AsyncData.Success( + persistentListOf( + VideoUploadEstimation(VideoCompressionPreset.HIGH, sizeInBytes = 100, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.STANDARD, sizeInBytes = 50, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.LOW, sizeInBytes = 25, canUpload = true), + ) + ) + ) + + assertThat(result.dataOrNull()).isEqualTo(VideoCompressionPreset.HIGH) + } + + @Test + fun `selectBestVideoPreset - falls back to the highest fitting preset`() { + val result = selector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = AsyncData.Success( + persistentListOf( + VideoUploadEstimation(VideoCompressionPreset.HIGH, sizeInBytes = 100, canUpload = false), + VideoUploadEstimation(VideoCompressionPreset.STANDARD, sizeInBytes = 50, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.LOW, sizeInBytes = 25, canUpload = true), + ) + ) + ) + + assertThat(result.dataOrNull()).isEqualTo(VideoCompressionPreset.STANDARD) + } + + @Test + fun `selectBestVideoPreset - starts from the expected preset`() { + val result = selector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.STANDARD, + videoSizeEstimations = AsyncData.Success( + persistentListOf( + VideoUploadEstimation(VideoCompressionPreset.HIGH, sizeInBytes = 100, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.STANDARD, sizeInBytes = 50, canUpload = true), + VideoUploadEstimation(VideoCompressionPreset.LOW, sizeInBytes = 25, canUpload = true), + ) + ) + ) + + assertThat(result.dataOrNull()).isEqualTo(VideoCompressionPreset.STANDARD) + } + + @Test + fun `selectBestVideoPreset - returns failure when no preset can upload`() { + val result = selector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = AsyncData.Success( + persistentListOf( + VideoUploadEstimation(VideoCompressionPreset.HIGH, sizeInBytes = 100, canUpload = false), + VideoUploadEstimation(VideoCompressionPreset.STANDARD, sizeInBytes = 50, canUpload = false), + VideoUploadEstimation(VideoCompressionPreset.LOW, sizeInBytes = 25, canUpload = false), + ) + ) + ) + + assertThat(result).isInstanceOf(AsyncData.Failure::class.java) + } + + @Test + fun `selectBestVideoPreset - returns loading while estimations are missing`() { + val result = selector.selectBestVideoPreset( + expectedVideoPreset = VideoCompressionPreset.HIGH, + videoSizeEstimations = AsyncData.Loading(), + ) + + assertThat(result).isInstanceOf(AsyncData.Loading::class.java) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MediaAttachmentFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MediaAttachmentFixtures.kt index 77207d6b52..1dde33714f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MediaAttachmentFixtures.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/MediaAttachmentFixtures.kt @@ -11,6 +11,7 @@ package io.element.android.features.messages.impl.fixtures import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.mediaviewer.api.local.LocalMedia -fun aMediaAttachment(localMedia: LocalMedia) = Attachment.Media( +fun aMediaAttachment(localMedia: LocalMedia, sendAsFile: Boolean = false) = Attachment.Media( localMedia = localMedia, + sendAsFile = sendAsFile, ) diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt index 02a6918ad8..fff3ede5d3 100644 --- a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt +++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/attachments/video/FakeMediaOptimizationSelectorPresenterFactory.kt @@ -26,7 +26,7 @@ class FakeMediaOptimizationSelectorPresenterFactory( ) } ) : MediaOptimizationSelectorPresenter.Factory { - override fun create(localMedia: LocalMedia): MediaOptimizationSelectorPresenter { + override fun create(localMedia: LocalMedia, sendAsFile: Boolean): MediaOptimizationSelectorPresenter { return fakePresenter } } 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 c07ebb6ec9..3750060eef 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 @@ -31,6 +31,10 @@ class FakeMediaPreProcessor( var cleanUpCallCount = 0 private set + /** The [MediaOptimizationConfig] passed to the most recent [process] call, or `null` if it was never called. */ + var lastMediaOptimizationConfig: MediaOptimizationConfig? = null + private set + private var result: Result = Result.success( MediaUploadInfo.AnyFile( File("test"), @@ -51,6 +55,7 @@ class FakeMediaPreProcessor( ): Result = simulateLongTask { processLatch?.await() processCallCount++ + lastMediaOptimizationConfig = mediaOptimizationConfig result } From 44df2d2c17d25c508246e7a647407e72482a074c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 20 May 2026 17:23:30 +0200 Subject: [PATCH 214/471] [a11y] Improve accessibility of media controller --- .../impl/local/player/MediaPlayerControllerView.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index 8d94ba9d2e..1f2b0b93d2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -181,20 +181,26 @@ fun MediaPlayerControllerView( style = ElementTheme.typography.fontBodyXsMedium, ) if (state.canMute) { + val a11yUnmute = stringResource(CommonStrings.common_unmute) + val a11yMute = stringResource(CommonStrings.common_mute) IconButton( onClick = onToggleMute, + modifier = Modifier + .semantics { + stateDescription = if (state.isMuted) a11yUnmute else a11yMute + }, ) { if (state.isMuted) { Icon( imageVector = CompoundIcons.VolumeOffSolid(), tint = ElementTheme.colors.iconPrimary, - contentDescription = stringResource(CommonStrings.common_unmute) + contentDescription = null, ) } else { Icon( imageVector = CompoundIcons.VolumeOnSolid(), tint = ElementTheme.colors.iconPrimary, - contentDescription = stringResource(CommonStrings.common_mute) + contentDescription = null, ) } } From 19397b6fa7ed85182dd55eefee13dfab456ebbc2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 21 May 2026 09:10:43 +0200 Subject: [PATCH 215/471] Fix warning --- .../kotlin/io/element/android/libraries/qrcode/QrCodeImage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeImage.kt b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeImage.kt index e045e42f17..b50b202e2c 100644 --- a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeImage.kt +++ b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeImage.kt @@ -57,8 +57,8 @@ private fun BitMatrix.toBitmap( @Composable fun QrCodeImage( data: String, - forceMaxBrightness: Boolean = true, modifier: Modifier = Modifier, + forceMaxBrightness: Boolean = true, ) { if (forceMaxBrightness) { ForceMaxBrightness() From b4f23d9d9e1d994de465d0ec91d506cf9c372917 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 21 May 2026 09:14:18 +0200 Subject: [PATCH 216/471] Fix wrong 'modifier' usage. --- .../linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt index ee38342ce2..f2cd07f4a5 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/qrcode/ShowQrCodeView.kt @@ -65,13 +65,13 @@ fun ShowQrCodeView( horizontalAlignment = Alignment.CenterHorizontally, ) { AnimatedContent( + modifier = Modifier.size(220.dp), targetState = state.data.dataOrNull(), transitionSpec = { fadeIn().togetherWith(fadeOut()) } ) { data -> QrCodeOrLoading( - modifier = modifier.size(220.dp), data = data, ) } From eed183be5385456a572afd2b8cb7da5476253199 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 10:10:13 +0200 Subject: [PATCH 217/471] Update metro to v1.1.1 (#6832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update metro to v1.1.1 * Fix `@ContributesBinding` usage with `@AssistedInject` in `DefaultVideoMetadataExtractor` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .../messages/impl/attachments/video/VideoMetadataExtractor.kt | 1 - gradle/libs.versions.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt index a6945b5ebe..b0ff50dc72 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/VideoMetadataExtractor.kt @@ -30,7 +30,6 @@ interface VideoMetadataExtractor : AutoCloseable { } } -@ContributesBinding(AppScope::class) @AssistedInject class DefaultVideoMetadataExtractor( @ApplicationContext private val context: Context, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 90b90b562e..ff7eede510 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,7 +54,7 @@ haze = "1.7.2" dependencyAnalysis = "3.11.0" # DI -metro = "1.0.0" +metro = "1.1.1" # Auto service autoservice = "1.1.1" From 4607681c5f0143551ce222c27c7f4d0c23883edb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 08:15:40 +0000 Subject: [PATCH 218/471] Update dependency io.element.android:element-call-embedded to v0.19.4 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff7eede510..90740da3c5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -235,7 +235,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.19.3" +element_call_embedded = "io.element.android:element-call-embedded:0.19.4" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } From 7e42dd1529905467302d4769aa1808ce57c8867e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 12:27:29 +0200 Subject: [PATCH 219/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.20 (#6831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v26.05.20 * Fix API breaks: - Handle new `ClientBuildException.InvalidRawKey` variant. - `RoomInfo` now has a `fullyReadEventId` . --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .../android/features/space/impl/root/SpaceStateProvider.kt | 1 + gradle/libs.versions.toml | 2 +- .../io/element/android/libraries/matrix/api/room/RoomInfo.kt | 1 + .../libraries/matrix/impl/auth/AuthenticationException.kt | 1 + .../android/libraries/matrix/impl/room/RoomInfoMapper.kt | 1 + .../libraries/matrix/impl/fixtures/factories/RoomInfo.kt | 2 ++ .../android/libraries/matrix/impl/room/RoomInfoMapperTest.kt | 4 ++++ .../android/libraries/matrix/test/room/RoomInfoFixture.kt | 2 ++ .../android/libraries/matrix/test/room/RoomSummaryFixture.kt | 2 ++ 9 files changed, 15 insertions(+), 1 deletion(-) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt index 20f4918a98..6824cc6c10 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceStateProvider.kt @@ -140,6 +140,7 @@ private fun aSpaceInfo( privilegedCreatorRole = false, isLowPriority = false, activeCallIntentConsensus = CallIntentConsensus.None, + fullyReadEventId = null, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9e0ab8c30b..2c0785abdb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -179,7 +179,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.18" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.20" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt index b9ed8d61b1..e590976cab 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt @@ -79,6 +79,7 @@ data class RoomInfo( val privilegedCreatorRole: Boolean, val isLowPriority: Boolean, val activeCallIntentConsensus: CallIntentConsensus, + val fullyReadEventId: EventId?, ) { val aliases: List get() = listOfNotNull(canonicalAlias) + alternativeAliases diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 20dbf76a31..fac5227f6a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -28,6 +28,7 @@ fun Throwable.mapAuthenticationException(): AuthenticationException { } is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message) is ClientBuildException.EventCache -> AuthenticationException.Generic(message) + is ClientBuildException.InvalidRawKey -> AuthenticationException.Generic(message) } is OAuthException -> when (this) { is OAuthException.Generic -> AuthenticationException.OAuth(message) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index 0e9aadc65b..a972b3c130 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -77,6 +77,7 @@ class RoomInfoMapper { privilegedCreatorRole = it.privilegedCreatorsRole, isLowPriority = it.isLowPriority, activeCallIntentConsensus = it.activeRoomCallConsensusIntent.map(), + fullyReadEventId = it.fullyReadEventId?.let(::EventId) ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt index 2ce64154f7..cb5e221dc9 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt @@ -64,6 +64,7 @@ internal fun aRustRoomInfo( activeRoomCallConsensusIntent: RtcCallIntentConsensus = RtcCallIntentConsensus.None, activeServiceMembersCount: Int = 0, isDm: Boolean = false, + fullyReadEventId: String? = null, ) = RoomInfo( id = id, displayName = displayName, @@ -105,4 +106,5 @@ internal fun aRustRoomInfo( activeRoomCallConsensusIntent = activeRoomCallConsensusIntent, activeServiceMembersCount = activeServiceMembersCount.toULong(), isDm = isDm, + fullyReadEventId = fullyReadEventId, ) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt index 56b480d97f..dc3b16ca3c 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt @@ -87,6 +87,7 @@ class RoomInfoMapperTest { isLowPriority = true, activeRoomCallConsensusIntent = RtcCallIntentConsensus.Full(RtcCallIntent.AUDIO), isDm = true, + fullyReadEventId = AN_EVENT_ID.value, ) ) ).isEqualTo( @@ -138,6 +139,7 @@ class RoomInfoMapperTest { isLowPriority = true, activeCallIntentConsensus = CallIntentConsensus.Full(CallIntent.AUDIO), isDm = true, + fullyReadEventId = AN_EVENT_ID, ) ) } @@ -184,6 +186,7 @@ class RoomInfoMapperTest { isLowPriority = true, activeRoomCallConsensusIntent = RtcCallIntentConsensus.None, isDm = false, + fullyReadEventId = AN_EVENT_ID.value, ) ) ).isEqualTo( @@ -229,6 +232,7 @@ class RoomInfoMapperTest { isLowPriority = true, activeCallIntentConsensus = CallIntentConsensus.None, isDm = false, + fullyReadEventId = AN_EVENT_ID, ) ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt index c15330e9dc..3293570eec 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -72,6 +72,7 @@ fun aRoomInfo( isLowPriority: Boolean = false, activeCallIntentConsensus: CallIntentConsensus = CallIntentConsensus.None, isDm: Boolean = false, + fullyReadEventId: EventId? = null, ) = RoomInfo( id = id, name = name, @@ -111,4 +112,5 @@ fun aRoomInfo( isLowPriority = isLowPriority, activeCallIntentConsensus = activeCallIntentConsensus, isDm = isDm, + fullyReadEventId = fullyReadEventId, ) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 32635a7eea..392aa880c9 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -81,6 +81,7 @@ fun aRoomSummary( privilegedCreatorRole: Boolean = false, isLowPriority: Boolean = false, activeCallIntentConsensus: CallIntentConsensus = CallIntentConsensus.None, + fullyReadEventId: EventId? = null, ) = RoomSummary( info = RoomInfo( id = roomId, @@ -121,6 +122,7 @@ fun aRoomSummary( isLowPriority = isLowPriority, activeCallIntentConsensus = activeCallIntentConsensus, isDm = false, + fullyReadEventId = fullyReadEventId, ), latestEvent = latestEvent, ) From d6ef5052603d040a8f5b4028e5a2edcf191e23d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 15:01:00 +0200 Subject: [PATCH 220/471] Merge pull request #6816 from element-hq/renovate/media3 Update media3 to v1.10.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2c0785abdb..3d49f2cda1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ constraintlayout = "2.2.1" constraintlayout_compose = "1.1.1" lifecycle = "2.10.0" activity = "1.13.0" -media3 = "1.10.0" +media3 = "1.10.1" camera = "1.6.1" work = "2.11.2" From 00efd9a01c97d6d55d885405d170b7314feca61b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 15:01:35 +0200 Subject: [PATCH 221/471] Update peaceiris/actions-gh-pages action to v4.1.0 (#6820) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/generate_github_pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index 55a300dd88..8de21b3ef1 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -36,7 +36,7 @@ jobs: mkdir -p screenshots/en cp tests/uitests/src/test/snapshots/images/* screenshots/en - name: Deploy GitHub Pages - uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./screenshots From bcad1f9dce2bebd9ae76a03ddf9d98309880b410 Mon Sep 17 00:00:00 2001 From: Gianluca Iavicoli Date: Thu, 21 May 2026 15:08:26 +0200 Subject: [PATCH 222/471] Add crop and rotate editing before sending images (#6363) * feat(messages): add crop and rotate before image upload * Update screenshots * chore: trigger CI after screenshot update * fix: resolve detekt violations in image editor and media viewer modules * fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject * fix: require explicit edits param, use plurals for rotation a11y, remove redundant @Inject * fix: use semantically correct RotateRight icon for image rotation action * Update screenshots * chore: trigger CI after screenshot update --------- Co-authored-by: ElementBot Co-authored-by: Benoit Marty --- features/messages/impl/build.gradle.kts | 1 + .../preview/AttachmentMediaInfo.kt | 44 ++ .../preview/AttachmentsPreviewEvent.kt | 8 + .../preview/AttachmentsPreviewPresenter.kt | 151 ++++- .../preview/AttachmentsPreviewState.kt | 5 + .../AttachmentsPreviewStateProvider.kt | 10 + .../preview/AttachmentsPreviewView.kt | 121 +++- .../imageeditor/AttachmentImageEditModels.kt | 126 +++++ .../imageeditor/AttachmentImageEditor.kt | 191 +++++++ .../imageeditor/AttachmentImageEditorView.kt | 522 ++++++++++++++++++ .../impl/src/main/res/values/temporary.xml | 8 + .../AttachmentsPreviewPresenterTest.kt | 225 ++++++++ .../AttachmentImageEditModelsTest.kt | 78 +++ .../impl/local/AndroidLocalMediaFactory.kt | 41 +- .../local/AndroidLocalMediaFactoryTest.kt | 29 +- ...tor_AttachmentImageEditorView_Day_0_en.png | 3 + ...r_AttachmentImageEditorView_Night_0_en.png | 3 + 17 files changed, 1517 insertions(+), 49 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentMediaInfo.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt create mode 100644 features/messages/impl/src/main/res/values/temporary.xml create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 6ff7f7e322..2661f7e330 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { implementation(libs.jsoup) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout.compose) + implementation(libs.androidx.exifinterface) implementation(libs.androidx.media3.exoplayer) implementation(libs.androidx.media3.ui) implementation(libs.sigpwned.emoji4j) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentMediaInfo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentMediaInfo.kt new file mode 100644 index 0000000000..7feeff18dd --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentMediaInfo.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview + +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAnimatedImage +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage +import io.element.android.libraries.mediaviewer.api.MediaInfo +import java.util.Locale + +internal fun MediaInfo.canEditImage(): Boolean { + val resolvedMimeType = resolvedImageMimeType() ?: return false + return resolvedMimeType.isMimeTypeImage() && + !resolvedMimeType.isMimeTypeAnimatedImage() && + resolvedMimeType != MimeTypes.Svg +} + +internal fun MediaInfo.isImageAttachment(): Boolean { + return resolvedImageMimeType().isMimeTypeImage() +} + +internal fun MediaInfo.resolvedImageMimeType(): String? { + return mimeType.takeIf { it.isMimeTypeImage() } ?: fileExtension.toImageMimeTypeOrNull() +} + +private fun String.toImageMimeTypeOrNull(): String? { + return when (lowercase(Locale.ROOT)) { + "png" -> MimeTypes.Png + "jpg", "jpeg" -> MimeTypes.Jpeg + "gif" -> MimeTypes.Gif + "webp" -> MimeTypes.WebP + "svg" -> MimeTypes.Svg + "bmp" -> "image/bmp" + "heic" -> "image/heic" + "heif" -> "image/heif" + "avif" -> "image/avif" + else -> null + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt index d473d4c3f4..1957a8b0f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt @@ -8,8 +8,16 @@ package io.element.android.features.messages.impl.attachments.preview +import io.element.android.features.messages.impl.attachments.preview.imageeditor.NormalizedCropRect + sealed interface AttachmentsPreviewEvent { data object SendAttachment : AttachmentsPreviewEvent data object CancelAndDismiss : AttachmentsPreviewEvent data object CancelAndClearSendState : AttachmentsPreviewEvent + data object OpenImageEditor : AttachmentsPreviewEvent + data object CloseImageEditor : AttachmentsPreviewEvent + data object RotateImage : AttachmentsPreviewEvent + data object ApplyImageEdits : AttachmentsPreviewEvent + data class UpdateImageCropRect(val cropRect: NormalizedCropRect) : AttachmentsPreviewEvent + data object ClearImageEditError : AttachmentsPreviewEvent } 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 fc7f3034a6..b5cb13e3e3 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 @@ -22,6 +22,9 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditor +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditorState +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorPresenter import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector @@ -32,7 +35,6 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.firstInstanceOf import io.element.android.libraries.core.extensions.runCatchingExceptions -import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.core.EventId @@ -51,7 +53,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber +import java.io.File @AssistedInject class AttachmentsPreviewPresenter( @@ -62,6 +66,7 @@ class AttachmentsPreviewPresenter( mediaSenderFactory: MediaSenderFactory, private val permalinkBuilder: PermalinkBuilder, private val temporaryUriDeleter: TemporaryUriDeleter, + private val attachmentImageEditor: AttachmentImageEditor, private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory, private val videoCompressionPresetSelector: VideoCompressionPresetSelector, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, @@ -87,6 +92,14 @@ class AttachmentsPreviewPresenter( val sendActionState = remember { mutableStateOf(SendActionState.Idle) } + val originalLocalMedia = remember { (attachment as Attachment.Media).localMedia } + var currentAttachment by remember { mutableStateOf(attachment) } + var canEditImage by remember { mutableStateOf(originalLocalMedia.info.canEditImage()) } + var imageEditorState by remember { mutableStateOf(null) } + var appliedImageEdits by remember { mutableStateOf(AttachmentImageEdits()) } + var isApplyingImageEdits by remember { mutableStateOf(false) } + var displayImageEditError by remember { mutableStateOf(false) } + var editedTempFile by remember { mutableStateOf(null) } val markdownTextEditorState = rememberMarkdownTextEditorState(initialText = null, initialFocus = false) val textEditorState by rememberUpdatedState( @@ -97,7 +110,7 @@ class AttachmentsPreviewPresenter( var preprocessMediaJob by remember { mutableStateOf(null) } - val mediaAttachment = attachment as Attachment.Media + val mediaAttachment = currentAttachment as Attachment.Media val mediaOptimizationSelectorPresenter = remember { mediaOptimizationSelectorPresenterFactory.create( localMedia = mediaAttachment.localMedia, @@ -113,11 +126,17 @@ class AttachmentsPreviewPresenter( LaunchedEffect( mediaOptimizationSelectorState.displayMediaSelectorViews, mediaOptimizationSelectorState.videoSizeEstimations, + currentAttachment, + imageEditorState, + isApplyingImageEdits, ) { // If the media optimization selector is not displayed, we can pre-process the media // to prepare it for sending. This is done to avoid blocking the UI thread when the // user clicks on the send button. - if (mediaOptimizationSelectorState.displayMediaSelectorViews == false && preprocessMediaJob == null) { + if (mediaOptimizationSelectorState.displayMediaSelectorViews == false && + preprocessMediaJob == null && + imageEditorState == null && + !isApplyingImageEdits) { if (mediaAttachment.localMedia.info.mimeType.isMimeTypeVideo() && mediaOptimizationSelectorState.videoSizeEstimations.dataOrNull() == null) { Timber.d("Waiting for video size estimations to be able to select the best video compression preset before pre-processing the media") return@LaunchedEffect @@ -127,7 +146,7 @@ class AttachmentsPreviewPresenter( mediaOptimizationSelectorState = mediaOptimizationSelectorState, ) ?: return@LaunchedEffect preprocessMediaJob = coroutineScope.preProcessAttachment( - attachment = attachment, + attachment = currentAttachment, mediaOptimizationConfig = config, displayProgress = false, sendActionState = sendActionState, @@ -135,10 +154,14 @@ class AttachmentsPreviewPresenter( } } + LaunchedEffect(originalLocalMedia) { + canEditImage = originalLocalMedia.info.canEditImage() || attachmentImageEditor.canEdit(originalLocalMedia) + } + val maxUploadSize = mediaOptimizationSelectorState.maxUploadSize.dataOrNull() LaunchedEffect(maxUploadSize) { // Check file upload size if the media won't be processed for upload - val isImageFile = mediaAttachment.localMedia.info.mimeType.isMimeTypeImage() + val isImageFile = mediaAttachment.localMedia.info.isImageAttachment() val isVideoFile = mediaAttachment.localMedia.info.mimeType.isMimeTypeVideo() if (maxUploadSize != null && !(isImageFile || isVideoFile)) { // If file size is not known, we're permissive and allow sending. The SDK will cancel the upload if needed. @@ -169,7 +192,7 @@ class AttachmentsPreviewPresenter( videoCompressionPreset = mediaOptimizationSelectorState.selectedVideoPreset ?: VideoCompressionPreset.STANDARD, ) preprocessMediaJob = preProcessAttachment( - attachment = attachment, + attachment = currentAttachment, mediaOptimizationConfig = config, displayProgress = true, sendActionState = sendActionState, @@ -188,6 +211,9 @@ class AttachmentsPreviewPresenter( val caption = markdownTextEditorState.getMessageMarkdown(permalinkBuilder) .takeIf { it.isNotEmpty() } + val editedTempFileToDelete = editedTempFile + editedTempFile = null + // If we're supposed to send the media as a background job, we can dismiss this screen already if (coroutineContext.isActive) { onDoneListener() @@ -195,33 +221,36 @@ class AttachmentsPreviewPresenter( // Send the media using the session coroutine scope so it doesn't matter if this screen or the chat one are closed sessionCoroutineScope.launch(dispatchers.io) { - sendPreProcessedMedia( - mediaUploadInfo = mediaUploadInfo, - caption = caption, - sendActionState = sendActionState, - dismissAfterSend = false, - inReplyToEventId = inReplyToEventId, - ) - - // Clean up the pre-processed media after it's been sent - mediaSender.cleanUp() + try { + sendPreProcessedMedia( + mediaUploadInfo = mediaUploadInfo, + caption = caption, + sendActionState = sendActionState, + dismissAfterSend = false, + inReplyToEventId = inReplyToEventId, + ) + } finally { + editedTempFileToDelete?.safeDelete() + // Clean up the pre-processed media after it's been sent + mediaSender.cleanUp() + } } } } AttachmentsPreviewEvent.CancelAndDismiss -> { displayFileTooLargeError = false + displayImageEditError = false + isApplyingImageEdits = false // Cancel media preprocessing and sending preprocessMediaJob?.cancel() + preprocessMediaJob = null // If we couldn't send the pre-processed media, remove it mediaSender.cleanUp() ongoingSendAttachmentJob.value?.cancel() // Dismiss the screen - dismiss( - attachment, - sendActionState, - ) + dismiss(sendActionState, editedTempFile) } AttachmentsPreviewEvent.CancelAndClearSendState -> { // Cancel media sending @@ -237,11 +266,82 @@ class AttachmentsPreviewPresenter( SendActionState.Idle } } + AttachmentsPreviewEvent.OpenImageEditor -> { + val resolvedCanEditImage = canEditImage || originalLocalMedia.info.canEditImage() + if (resolvedCanEditImage) { + preprocessMediaJob?.cancel() + preprocessMediaJob = null + resetPreparedMedia(sendActionState) + imageEditorState = AttachmentImageEditorState( + localMedia = originalLocalMedia, + edits = appliedImageEdits, + ) + } + } + AttachmentsPreviewEvent.CloseImageEditor -> { + imageEditorState = null + } + is AttachmentsPreviewEvent.UpdateImageCropRect -> { + val pendingState = imageEditorState ?: return + imageEditorState = pendingState.copy( + edits = pendingState.edits.copy(cropRect = event.cropRect) + ) + } + AttachmentsPreviewEvent.RotateImage -> { + val pendingState = imageEditorState ?: return + imageEditorState = pendingState.copy( + edits = pendingState.edits.rotateClockwise() + ) + } + AttachmentsPreviewEvent.ApplyImageEdits -> { + val pendingState = imageEditorState ?: return + if (!pendingState.edits.hasChanges) { + editedTempFile?.safeDelete() + editedTempFile = null + appliedImageEdits = pendingState.edits + currentAttachment = Attachment.Media(originalLocalMedia) + imageEditorState = null + resetPreparedMedia(sendActionState) + return + } + isApplyingImageEdits = true + displayImageEditError = false + coroutineScope.launch { + val result = withContext(dispatchers.io) { + attachmentImageEditor.exportEdits( + localMedia = originalLocalMedia, + edits = pendingState.edits, + ) + } + result.fold( + onSuccess = { editedMedia -> + editedTempFile?.safeDelete() + editedTempFile = editedMedia.file + appliedImageEdits = pendingState.edits + currentAttachment = Attachment.Media(editedMedia.localMedia) + imageEditorState = null + resetPreparedMedia(sendActionState) + }, + onFailure = { + Timber.e(it, "Failed to apply image edits") + displayImageEditError = true + } + ) + isApplyingImageEdits = false + } + } + AttachmentsPreviewEvent.ClearImageEditError -> { + displayImageEditError = false + } } } return AttachmentsPreviewState( - attachment = attachment, + attachment = currentAttachment, + imageEditorState = imageEditorState, + canEditImage = canEditImage, + isApplyingImageEdits = isApplyingImageEdits, + displayImageEditError = displayImageEditError, sendActionState = sendActionState.value, textEditorState = textEditorState, mediaOptimizationSelectorState = mediaOptimizationSelectorState, @@ -318,8 +418,8 @@ class AttachmentsPreviewPresenter( } private fun dismiss( - attachment: Attachment, sendActionState: MutableState, + editedTempFile: File?, ) { // Delete the temporary file when (attachment) { @@ -330,6 +430,7 @@ class AttachmentsPreviewPresenter( } } } + editedTempFile?.safeDelete() // Reset the sendActionState to ensure that dialog is closed before the screen sendActionState.value = SendActionState.Done onDoneListener() @@ -343,6 +444,12 @@ class AttachmentsPreviewPresenter( } } + private fun resetPreparedMedia(sendActionState: MutableState) { + sendActionState.value.mediaUploadInfo()?.let(::cleanUp) + mediaSender.cleanUp() + sendActionState.value = SendActionState.Idle + } + private suspend fun sendPreProcessedMedia( mediaUploadInfo: MediaUploadInfo, caption: String?, 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 97ca230d77..463479fe55 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 @@ -10,12 +10,17 @@ 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.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditorState import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.textcomposer.model.TextEditorState data class AttachmentsPreviewState( val attachment: Attachment, + val imageEditorState: AttachmentImageEditorState?, + val canEditImage: Boolean, + val isApplyingImageEdits: Boolean, + val displayImageEditError: Boolean, val sendActionState: SendActionState, val textEditorState: TextEditorState, val mediaOptimizationSelectorState: MediaOptimizationSelectorState, 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 70d7ab006e..ced90550c3 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 @@ -11,6 +11,8 @@ package io.element.android.features.messages.impl.attachments.preview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditorState +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation import io.element.android.libraries.architecture.AsyncData @@ -42,6 +44,9 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider false + is SendActionState.Sending.Processing -> !state.sendActionState.displayProgress + SendActionState.Done -> false + else -> true + } + fun postSendAttachment() { state.eventSink(AttachmentsPreviewEvent.SendAttachment) } @@ -93,33 +102,75 @@ fun AttachmentsPreviewView( state.eventSink(AttachmentsPreviewEvent.CancelAndClearSendState) } - BackHandler(enabled = state.sendActionState !is SendActionState.Sending.Uploading && state.sendActionState !is SendActionState.Done) { - postCancel() + fun postOpenImageEditor() { + state.eventSink(AttachmentsPreviewEvent.OpenImageEditor) } - Scaffold( - modifier = modifier, - topBar = { - TopAppBar( - navigationIcon = { - BackButton( - imageVector = CompoundIcons.Close(), - onClick = ::postCancel, - ) - }, - title = {}, + fun postCloseImageEditor() { + state.eventSink(AttachmentsPreviewEvent.CloseImageEditor) + } + + fun postApplyImageEdits() { + state.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) + } + + BackHandler(enabled = state.sendActionState !is SendActionState.Sending.Uploading && state.sendActionState !is SendActionState.Done) { + if (state.imageEditorState != null) { + postCloseImageEditor() + } else { + postCancel() + } + } + + if (state.imageEditorState != null) { + AttachmentImageEditorView( + state = state.imageEditorState, + onCropRectChange = { cropRect -> + state.eventSink(AttachmentsPreviewEvent.UpdateImageCropRect(cropRect)) + }, + onRotateClick = { state.eventSink(AttachmentsPreviewEvent.RotateImage) }, + onCancelClick = ::postCloseImageEditor, + onDoneClick = ::postApplyImageEdits, + modifier = modifier, + ) + } else { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + navigationIcon = { + BackButton( + imageVector = CompoundIcons.Close(), + onClick = ::postCancel, + ) + }, + title = {}, + actions = { + if (state.canEditImage && canShowEditAction) { + IconButton(onClick = ::postOpenImageEditor) { + Icon( + imageVector = CompoundIcons.Edit(), + contentDescription = stringResource(CommonStrings.action_edit), + ) + } + } + } + ) + } + ) { paddingValues -> + AttachmentPreviewContent( + modifier = Modifier.padding(paddingValues), + state = state, + localMediaRenderer = localMediaRenderer, + onSendClick = ::postSendAttachment, ) } - ) { paddingValues -> - AttachmentPreviewContent( - modifier = Modifier.padding(paddingValues), - state = state, - localMediaRenderer = localMediaRenderer, - onSendClick = ::postSendAttachment, - ) } AttachmentSendStateView( sendActionState = state.sendActionState, + isApplyingImageEdits = state.isApplyingImageEdits, + displayImageEditError = state.displayImageEditError, + onDismissImageEditError = { state.eventSink(AttachmentsPreviewEvent.ClearImageEditError) }, onDismissClick = ::postClearSendState, onRetryClick = ::postSendAttachment ) @@ -128,10 +179,29 @@ fun AttachmentsPreviewView( @Composable private fun AttachmentSendStateView( sendActionState: SendActionState, + isApplyingImageEdits: Boolean, + displayImageEditError: Boolean, + onDismissImageEditError: () -> Unit, onDismissClick: () -> Unit, onRetryClick: () -> Unit ) { - when (sendActionState) { + when { + isApplyingImageEdits -> { + ProgressDialog( + type = ProgressDialogType.Indeterminate, + text = stringResource(CommonStrings.common_preparing), + showCancelButton = false, + onDismissRequest = {}, + ) + } + displayImageEditError -> { + AlertDialog( + title = stringResource(CommonStrings.common_error), + content = stringResource(CommonStrings.common_something_went_wrong_message), + onDismiss = onDismissImageEditError, + ) + } + else -> when (sendActionState) { is SendActionState.Sending.Processing -> { if (sendActionState.displayProgress) { ProgressDialog( @@ -158,6 +228,7 @@ private fun AttachmentSendStateView( ) } else -> Unit + } } } @@ -184,10 +255,10 @@ private fun AttachmentPreviewContent( } } } - val mimeType = (state.attachment as? Attachment.Media)?.localMedia?.info?.mimeType - if (mimeType?.isMimeTypeImage() == true) { + val mediaInfo = (state.attachment as? Attachment.Media)?.localMedia?.info + if (mediaInfo?.isImageAttachment() == true) { ImageOptimizationSelector(state.mediaOptimizationSelectorState) - } else if (mimeType?.isMimeTypeVideo() == true) { + } else if (mediaInfo?.mimeType?.isMimeTypeVideo() == true) { VideoPresetSelector(state = state.mediaOptimizationSelectorState) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt new file mode 100644 index 0000000000..90872df4f2 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.mediaviewer.api.local.LocalMedia + +private const val DEFAULT_CROP_MARGIN = 0.1f +private const val MIN_CROP_SIZE = 0.1f + +@Immutable +data class AttachmentImageEditorState( + val localMedia: LocalMedia, + val edits: AttachmentImageEdits, +) + +@Immutable +data class AttachmentImageEdits( + val cropRect: NormalizedCropRect = NormalizedCropRect.default(), + val rotationQuarterTurns: Int = 0, +) { + val normalizedRotationQuarterTurns: Int + get() = (rotationQuarterTurns % 4 + 4) % 4 + + val rotationDegrees: Int + get() = normalizedRotationQuarterTurns * 90 + + val hasChanges: Boolean + get() = cropRect != NormalizedCropRect.default() || normalizedRotationQuarterTurns != 0 + + fun rotateClockwise(): AttachmentImageEdits { + return copy(rotationQuarterTurns = (normalizedRotationQuarterTurns + 1) % 4) + } +} + +@Immutable +data class NormalizedCropRect( + val left: Float, + val top: Float, + val right: Float, + val bottom: Float, +) { + init { + require(left in 0f..1f) + require(top in 0f..1f) + require(right in 0f..1f) + require(bottom in 0f..1f) + require(left < right) + require(top < bottom) + } + + val width: Float + get() = right - left + + val height: Float + get() = bottom - top + + fun translate(deltaX: Float, deltaY: Float): NormalizedCropRect { + val clampedLeft = (left + deltaX).coerceIn(0f, 1f - width) + val clampedTop = (top + deltaY).coerceIn(0f, 1f - height) + return copy( + left = clampedLeft, + top = clampedTop, + right = clampedLeft + width, + bottom = clampedTop + height, + ) + } + + fun resize(dragTarget: CropDragTarget, deltaX: Float, deltaY: Float): NormalizedCropRect = when (dragTarget) { + CropDragTarget.Move -> translate(deltaX, deltaY) + CropDragTarget.TopLeft -> copy( + left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), + top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), + ) + CropDragTarget.Top -> copy( + top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), + ) + CropDragTarget.TopRight -> copy( + right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), + top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), + ) + CropDragTarget.Right -> copy( + right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.BottomRight -> copy( + right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), + bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.Bottom -> copy( + bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.BottomLeft -> copy( + left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), + bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.Left -> copy( + left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), + ) + } + + companion object { + fun default() = NormalizedCropRect( + left = DEFAULT_CROP_MARGIN, + top = DEFAULT_CROP_MARGIN, + right = 1f - DEFAULT_CROP_MARGIN, + bottom = 1f - DEFAULT_CROP_MARGIN, + ) + } +} + +enum class CropDragTarget { + Move, + TopLeft, + Top, + TopRight, + Right, + BottomRight, + Bottom, + BottomLeft, + Left, +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt new file mode 100644 index 0000000000..f003c19084 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Matrix +import android.net.Uri +import androidx.exifinterface.media.ExifInterface +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.features.messages.impl.attachments.preview.resolvedImageMimeType +import io.element.android.libraries.androidutils.bitmap.rotateToExifMetadataOrientation +import io.element.android.libraries.androidutils.bitmap.writeBitmap +import io.element.android.libraries.androidutils.file.createTmpFile +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAnimatedImage +import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import kotlinx.coroutines.withContext +import java.io.File +import kotlin.math.roundToInt + +private const val EDITED_MEDIA_DIR_NAME = "edited-media" + +interface AttachmentImageEditor { + suspend fun canEdit(localMedia: LocalMedia): Boolean + + suspend fun exportEdits( + localMedia: LocalMedia, + edits: AttachmentImageEdits, + ): Result +} + +data class EditedLocalMedia( + val localMedia: LocalMedia, + val file: File, +) + +@ContributesBinding(AppScope::class) +class DefaultAttachmentImageEditor( + @ApplicationContext private val context: Context, + private val dispatchers: CoroutineDispatchers, +) : AttachmentImageEditor { + override suspend fun canEdit(localMedia: LocalMedia): Boolean = withContext(dispatchers.io) { + localMedia.info.resolvedImageMimeType() + ?.takeIf { it.isEditableStillImageMimeType() } + ?.let { return@withContext true } + + val decodedMimeType = context.contentResolver.openInputStream(localMedia.uri)?.use { input -> + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeStream(input, null, options) + options.outMimeType + } + + decodedMimeType.isEditableStillImageMimeType() + } + + override suspend fun exportEdits( + localMedia: LocalMedia, + edits: AttachmentImageEdits, + ): Result = withContext(dispatchers.io) { + runCatchingExceptions { + val sourceMimeType = localMedia.info.resolvedImageMimeType() ?: localMedia.info.mimeType + val exportedMimeType = exportedMimeTypeFor(sourceMimeType) + val exifOrientation = context.contentResolver.openInputStream(localMedia.uri)?.let { input -> + input.use { + ExifInterface(it).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED) + } + } ?: ExifInterface.ORIENTATION_UNDEFINED + + val decodedBitmap = context.contentResolver.openInputStream(localMedia.uri)?.use { input -> + BitmapFactory.decodeStream(input) + } ?: error("Unable to decode image from ${localMedia.uri}") + + val normalizedBitmap = decodedBitmap.rotateToExifMetadataOrientation(exifOrientation) + if (normalizedBitmap !== decodedBitmap) { + decodedBitmap.recycle() + } + + val rotatedBitmap = normalizedBitmap.rotateQuarterTurns(edits.rotationQuarterTurns) + if (rotatedBitmap !== normalizedBitmap) { + normalizedBitmap.recycle() + } + + val cropRect = edits.cropRect.toPixelRect( + imageWidth = rotatedBitmap.width, + imageHeight = rotatedBitmap.height, + ) + val isCropUnchanged = cropRect.left == 0 && cropRect.top == 0 && + cropRect.width() == rotatedBitmap.width && cropRect.height() == rotatedBitmap.height + val croppedBitmap = if (isCropUnchanged) { + rotatedBitmap + } else { + Bitmap.createBitmap( + rotatedBitmap, + cropRect.left, + cropRect.top, + cropRect.width(), + cropRect.height(), + ) + } + if (croppedBitmap !== rotatedBitmap) { + rotatedBitmap.recycle() + } + + val editedMediaDir = File(context.cacheDir, EDITED_MEDIA_DIR_NAME).apply { mkdirs() } + val outputFile = context.createTmpFile(baseDir = editedMediaDir, extension = compressFileExtension(exportedMimeType)) + outputFile.writeBitmap( + bitmap = croppedBitmap, + format = compressFormat(exportedMimeType), + quality = 90, + ) + croppedBitmap.recycle() + + EditedLocalMedia( + localMedia = localMedia.copy( + uri = Uri.fromFile(outputFile), + info = localMedia.info.copy(mimeType = exportedMimeType), + ), + file = outputFile, + ) + } + } +} + +internal fun exportedMimeTypeFor(sourceMimeType: String?): String { + return if (sourceMimeType == MimeTypes.Png) { + MimeTypes.Png + } else { + MimeTypes.Jpeg + } +} + +private fun Bitmap.rotateQuarterTurns(quarterTurns: Int): Bitmap { + val normalizedTurns = (quarterTurns % 4 + 4) % 4 + if (normalizedTurns == 0) return this + val matrix = Matrix().apply { + postRotate(normalizedTurns * 90f) + } + return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) +} + +private data class PixelCropRect( + val left: Int, + val top: Int, + val right: Int, + val bottom: Int, +) { + fun width() = right - left + fun height() = bottom - top +} + +private fun NormalizedCropRect.toPixelRect(imageWidth: Int, imageHeight: Int): PixelCropRect { + val leftPx = (left * imageWidth).roundToInt().coerceIn(0, imageWidth - 1) + val topPx = (top * imageHeight).roundToInt().coerceIn(0, imageHeight - 1) + val rightPx = (right * imageWidth).roundToInt().coerceIn(leftPx + 1, imageWidth) + val bottomPx = (bottom * imageHeight).roundToInt().coerceIn(topPx + 1, imageHeight) + return PixelCropRect( + left = leftPx, + top = topPx, + right = rightPx, + bottom = bottomPx, + ) +} + +private fun compressFormat(mimeType: String) = when (mimeType) { + "image/png" -> Bitmap.CompressFormat.PNG + else -> Bitmap.CompressFormat.JPEG +} + +private fun compressFileExtension(mimeType: String) = when (mimeType) { + "image/png" -> "png" + else -> "jpeg" +} + +private fun String?.isEditableStillImageMimeType(): Boolean { + return this != null && + this.isMimeTypeImage() && + !this.isMimeTypeAnimatedImage() && + this != MimeTypes.Svg +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt new file mode 100644 index 0000000000..bf3958e83f --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +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.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import coil3.compose.AsyncImage +import coil3.compose.AsyncImagePainter +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.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.TextButton +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.ui.strings.CommonStrings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AttachmentImageEditorView( + state: AttachmentImageEditorState, + onCropRectChange: (NormalizedCropRect) -> Unit, + onRotateClick: () -> Unit, + onCancelClick: () -> Unit, + onDoneClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val rotateContentDescription = stringResource(R.string.screen_media_upload_preview_rotate) + val rotationStateDescription = pluralStringResource( + R.plurals.a11y_media_upload_preview_rotation_degrees, + state.edits.rotationDegrees, + state.edits.rotationDegrees, + ) + val rotateButtonBackground = ElementTheme.colors.bgSubtlePrimary + + Scaffold( + modifier = modifier.fillMaxSize(), + topBar = { + TopAppBar( + navigationIcon = { + BackButton( + imageVector = CompoundIcons.Close(), + onClick = onCancelClick, + ) + }, + title = {}, + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .background(ElementTheme.colors.bgCanvasDefault) + .padding(paddingValues) + ) { + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + CropEditorCanvas( + state = state, + onCropRectChange = onCropRectChange, + ) + } + + Box( + modifier = Modifier + .fillMaxWidth() + .height(132.dp) + .background(ElementTheme.colors.bgCanvasDefault) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + .navigationBarsPadding() + .padding(start = 20.dp, top = 18.dp, end = 20.dp, bottom = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.CenterStart, + ) { + TextButton( + text = stringResource(CommonStrings.action_cancel), + onClick = onCancelClick, + ) + } + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + IconButton( + onClick = onRotateClick, + modifier = Modifier + .size(72.dp) + .background( + color = rotateButtonBackground, + shape = CircleShape, + ) + .clearAndSetSemantics { + contentDescription = rotateContentDescription + stateDescription = rotationStateDescription + } + ) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Spacer(modifier = Modifier.height(2.dp)) + Icon( + modifier = Modifier + .size(22.dp), + imageVector = CompoundIcons.RotateRight(), + contentDescription = null, + ) + Spacer(modifier = Modifier.height(3.dp)) + Text( + text = "${state.edits.rotationDegrees}°", + style = ElementTheme.typography.fontBodyXsMedium, + color = ElementTheme.colors.textSecondary, + ) + } + } + } + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.CenterEnd, + ) { + TextButton( + text = stringResource(CommonStrings.action_done), + onClick = onDoneClick, + ) + } + } + } + } + } +} + +@Composable +private fun CropEditorCanvas( + state: AttachmentImageEditorState, + onCropRectChange: (NormalizedCropRect) -> Unit, +) { + var imageSize by remember(state.localMedia.uri) { mutableStateOf(IntSize.Zero) } + val rotationQuarterTurns = state.edits.normalizedRotationQuarterTurns + + BoxWithConstraints( + modifier = Modifier + .fillMaxSize() + ) { + val displayedSize = remember(maxWidth, maxHeight, imageSize, rotationQuarterTurns) { + val sourceWidth = imageSize.width.takeIf { it > 0 } ?: 1 + val sourceHeight = imageSize.height.takeIf { it > 0 } ?: 1 + val aspectRatio = if (rotationQuarterTurns % 2 == 0) { + sourceWidth.toFloat() / sourceHeight.toFloat() + } else { + sourceHeight.toFloat() / sourceWidth.toFloat() + } + fitSize( + containerWidth = constraints.maxWidth.toFloat(), + containerHeight = constraints.maxHeight.toFloat(), + aspectRatio = aspectRatio, + ) + } + val density = LocalDensity.current + val displayedWidthDp = with(density) { displayedSize.width.toDp() } + val displayedHeightDp = with(density) { displayedSize.height.toDp() } + val imageLayoutSize = remember(displayedSize, rotationQuarterTurns) { + if (rotationQuarterTurns % 2 == 0) { + displayedSize + } else { + Size( + width = displayedSize.height, + height = displayedSize.width, + ) + } + } + val imageLayoutWidthDp = with(density) { imageLayoutSize.width.toDp() } + val imageLayoutHeightDp = with(density) { imageLayoutSize.height.toDp() } + + Box( + modifier = Modifier + .size(displayedWidthDp, displayedHeightDp) + .align(Alignment.Center), + contentAlignment = Alignment.Center, + ) { + if (LocalInspectionMode.current) { + Image( + painter = painterResource(id = CommonDrawables.sample_background), + contentDescription = null, + modifier = Modifier + .requiredSize(imageLayoutWidthDp, imageLayoutHeightDp) + .graphicsLayer { rotationZ = rotationQuarterTurns * 90f }, + contentScale = ContentScale.Fit, + ) + } else { + AsyncImage( + model = state.localMedia.uri, + contentDescription = stringResource(CommonStrings.common_image), + modifier = Modifier + .requiredSize(imageLayoutWidthDp, imageLayoutHeightDp) + .graphicsLayer { rotationZ = rotationQuarterTurns * 90f }, + contentScale = ContentScale.Fit, + onState = { painterState -> + if (painterState is AsyncImagePainter.State.Success) { + imageSize = IntSize( + width = painterState.result.image.width, + height = painterState.result.image.height, + ) + } + } + ) + } + + CropOverlay( + cropRect = state.edits.cropRect, + onCropRectChange = onCropRectChange, + ) + } + } +} + +@Composable +private fun CropOverlay( + cropRect: NormalizedCropRect, + onCropRectChange: (NormalizedCropRect) -> Unit, +) { + var dragTarget by remember { mutableStateOf(null) } + val latestCropRect by rememberUpdatedState(cropRect) + val borderColor = ElementTheme.colors.textPrimary + val guideColor = ElementTheme.colors.textSecondary + + Canvas( + modifier = Modifier + .fillMaxSize() + .pointerInput(Unit) { + detectDragGestures( + onDragStart = { offset -> + dragTarget = detectDragTarget( + touchPoint = offset, + cropRect = latestCropRect, + canvasSize = Size(size.width.toFloat(), size.height.toFloat()), + handleTouchRadius = 32.dp.toPx(), + ) + }, + onDragCancel = { + dragTarget = null + }, + onDragEnd = { + dragTarget = null + }, + ) { change, dragAmount -> + val activeTarget = dragTarget ?: return@detectDragGestures + change.consume() + onCropRectChange( + latestCropRect.resize( + dragTarget = activeTarget, + deltaX = dragAmount.x / size.width.toFloat(), + deltaY = dragAmount.y / size.height.toFloat(), + ) + ) + } + } + ) { + val cropLeft = cropRect.left * size.width + val cropTop = cropRect.top * size.height + val cropRight = cropRect.right * size.width + val cropBottom = cropRect.bottom * size.height + // Hardcoded black: the crop overlay must always darken the image regardless of theme. + // No semantic token exists for this use case in the Compound design system. + val overlayColor = Color.Black.copy(alpha = 0.48f) + + drawRect( + color = overlayColor, + topLeft = Offset.Zero, + size = Size(width = size.width, height = cropTop), + ) + drawRect( + color = overlayColor, + topLeft = Offset(0f, cropTop), + size = Size(width = cropLeft, height = cropBottom - cropTop), + ) + drawRect( + color = overlayColor, + topLeft = Offset(cropRight, cropTop), + size = Size(width = size.width - cropRight, height = cropBottom - cropTop), + ) + drawRect( + color = overlayColor, + topLeft = Offset(0f, cropBottom), + size = Size(width = size.width, height = size.height - cropBottom), + ) + + drawRect( + color = borderColor, + topLeft = Offset(cropLeft, cropTop), + size = Size(width = cropRight - cropLeft, height = cropBottom - cropTop), + style = Stroke(width = 2.dp.toPx()), + ) + + val thirdWidth = (cropRight - cropLeft) / 3f + val thirdHeight = (cropBottom - cropTop) / 3f + repeat(2) { index -> + val offsetX = cropLeft + thirdWidth * (index + 1) + val offsetY = cropTop + thirdHeight * (index + 1) + drawLine( + color = guideColor, + start = Offset(offsetX, cropTop), + end = Offset(offsetX, cropBottom), + strokeWidth = 1.dp.toPx(), + ) + drawLine( + color = guideColor, + start = Offset(cropLeft, offsetY), + end = Offset(cropRight, offsetY), + strokeWidth = 1.dp.toPx(), + ) + } + + val handleLength = 16.dp.toPx() + val handleColor = borderColor + drawCornerHandle(cropLeft, cropTop, handleLength, handleColor, true, true) + drawCornerHandle(cropRight, cropTop, handleLength, handleColor, false, true) + drawCornerHandle(cropLeft, cropBottom, handleLength, handleColor, true, false) + drawCornerHandle(cropRight, cropBottom, handleLength, handleColor, false, false) + drawEdgeHandle( + center = Offset((cropLeft + cropRight) / 2f, cropTop), + horizontal = true, + handleLength = handleLength, + color = handleColor, + ) + drawEdgeHandle( + center = Offset(cropRight, (cropTop + cropBottom) / 2f), + horizontal = false, + handleLength = handleLength, + color = handleColor, + ) + drawEdgeHandle( + center = Offset((cropLeft + cropRight) / 2f, cropBottom), + horizontal = true, + handleLength = handleLength, + color = handleColor, + ) + drawEdgeHandle( + center = Offset(cropLeft, (cropTop + cropBottom) / 2f), + horizontal = false, + handleLength = handleLength, + color = handleColor, + ) + } +} + +private fun fitSize( + containerWidth: Float, + containerHeight: Float, + aspectRatio: Float, +): Size { + val widthBasedHeight = containerWidth / aspectRatio + return if (widthBasedHeight <= containerHeight) { + Size(width = containerWidth, height = widthBasedHeight) + } else { + Size(width = containerHeight * aspectRatio, height = containerHeight) + } +} + +private fun detectDragTarget( + touchPoint: Offset, + cropRect: NormalizedCropRect, + canvasSize: Size, + handleTouchRadius: Float, +): CropDragTarget? { + val corners = mapOf( + CropDragTarget.TopLeft to Offset(cropRect.left * canvasSize.width, cropRect.top * canvasSize.height), + CropDragTarget.Top to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.top * canvasSize.height), + CropDragTarget.TopRight to Offset(cropRect.right * canvasSize.width, cropRect.top * canvasSize.height), + CropDragTarget.Right to Offset(cropRect.right * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), + CropDragTarget.BottomRight to Offset(cropRect.right * canvasSize.width, cropRect.bottom * canvasSize.height), + CropDragTarget.Bottom to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.bottom * canvasSize.height), + CropDragTarget.BottomLeft to Offset(cropRect.left * canvasSize.width, cropRect.bottom * canvasSize.height), + CropDragTarget.Left to Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), + ) + corners.forEach { (target, corner) -> + if ((corner - touchPoint).getDistance() <= handleTouchRadius) { + return target + } + } + val cropLeft = cropRect.left * canvasSize.width + val cropTop = cropRect.top * canvasSize.height + val cropRight = cropRect.right * canvasSize.width + val cropBottom = cropRect.bottom * canvasSize.height + return if (touchPoint.x in cropLeft..cropRight && touchPoint.y in cropTop..cropBottom) { + CropDragTarget.Move + } else { + null + } +} + +private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawCornerHandle( + x: Float, + y: Float, + handleLength: Float, + color: Color, + isLeft: Boolean, + isTop: Boolean, +) { + val horizontalEndX = if (isLeft) x + handleLength else x - handleLength + val verticalEndY = if (isTop) y + handleLength else y - handleLength + drawLine( + color = color, + start = Offset(x, y), + end = Offset(horizontalEndX, y), + strokeWidth = 3.dp.toPx(), + ) + drawLine( + color = color, + start = Offset(x, y), + end = Offset(x, verticalEndY), + strokeWidth = 3.dp.toPx(), + ) +} + +private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawEdgeHandle( + center: Offset, + horizontal: Boolean, + handleLength: Float, + color: Color, +) { + val start = if (horizontal) { + Offset(center.x - handleLength / 2f, center.y) + } else { + Offset(center.x, center.y - handleLength / 2f) + } + val end = if (horizontal) { + Offset(center.x + handleLength / 2f, center.y) + } else { + Offset(center.x, center.y + handleLength / 2f) + } + drawLine( + color = color, + start = start, + end = end, + strokeWidth = 3.dp.toPx(), + ) +} + +@PreviewsDayNight +@Composable +internal fun AttachmentImageEditorViewPreview() = ElementPreview { + AttachmentImageEditorView( + state = AttachmentImageEditorState( + localMedia = LocalMedia( + uri = "file://preview-image".toUri(), + info = anImageMediaInfo(), + ), + edits = AttachmentImageEdits(), + ), + onCropRectChange = {}, + onRotateClick = {}, + onCancelClick = {}, + onDoneClick = {}, + ) +} diff --git a/features/messages/impl/src/main/res/values/temporary.xml b/features/messages/impl/src/main/res/values/temporary.xml new file mode 100644 index 0000000000..f0050224a9 --- /dev/null +++ b/features/messages/impl/src/main/res/values/temporary.xml @@ -0,0 +1,8 @@ + + + Rotate + + %1$d degree + %1$d degrees + + 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 9a9dc08834..5cbfd331c1 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 @@ -11,11 +11,16 @@ package io.element.android.features.messages.impl.attachments import android.net.Uri +import androidx.core.net.toUri import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvent 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.attachments.preview.imageeditor.AttachmentImageEditor +import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits +import io.element.android.features.messages.impl.attachments.preview.imageeditor.EditedLocalMedia +import io.element.android.features.messages.impl.attachments.preview.imageeditor.NormalizedCropRect import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation @@ -73,6 +78,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import java.io.File +import kotlin.io.path.createTempFile @Suppress("LargeClass") @RunWith(RobolectricTestRunner::class) @@ -551,6 +557,85 @@ class AttachmentsPreviewPresenterTest { } @Test + fun `present - applying image edits updates the attachment`() = runTest { + val editedUri = Uri.parse("file:///tmp/edited.jpeg") + val presenter = createAttachmentsPreviewPresenter( + displayMediaQualitySelectorViews = true, + attachmentImageEditor = FakeAttachmentImageEditor { + Result.success( + EditedLocalMedia( + localMedia = aLocalMedia(uri = editedUri), + file = File("/tmp/edited.jpeg"), + ) + ) + } + ) + + presenter.test { + val initialState = awaitItem() + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = awaitItem() + assertThat(editorState.imageEditorState).isNotNull() + + editorState.eventSink(AttachmentsPreviewEvent.RotateImage) + val rotatedState = awaitItem() + assertThat(rotatedState.imageEditorState?.edits?.rotationQuarterTurns).isEqualTo(1) + + rotatedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) + assertThat(awaitItem().isApplyingImageEdits).isTrue() + + val appliedState = awaitItem() + assertThat((appliedState.attachment as Attachment.Media).localMedia.uri).isEqualTo(editedUri) + assertThat(appliedState.imageEditorState).isNull() + assertThat(appliedState.isApplyingImageEdits).isFalse() + } + } + + @Test + fun `present - reopening image editor keeps original media and previous edits`() = runTest { + val editedUri = Uri.parse("file:///tmp/edited.jpeg") + val originalLocalMedia = aLocalMedia(uri = mockMediaUrl) + val cropRect = NormalizedCropRect( + left = 0.2f, + top = 0.15f, + right = 0.85f, + bottom = 0.9f, + ) + val presenter = createAttachmentsPreviewPresenter( + localMedia = originalLocalMedia, + displayMediaQualitySelectorViews = true, + attachmentImageEditor = FakeAttachmentImageEditor { + Result.success( + EditedLocalMedia( + localMedia = aLocalMedia(uri = editedUri), + file = File("/tmp/edited.jpeg"), + ) + ) + } + ) + + presenter.test { + val initialState = awaitItem() + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + + editorState.eventSink(AttachmentsPreviewEvent.UpdateImageCropRect(cropRect)) + val croppedState = awaitItem() + croppedState.eventSink(AttachmentsPreviewEvent.RotateImage) + val rotatedState = awaitItem() + rotatedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) + + val appliedState = consumeItemsUntilPredicate { !it.isApplyingImageEdits && it.imageEditorState == null }.last() + assertThat((appliedState.attachment as Attachment.Media).localMedia.uri).isEqualTo(editedUri) + + appliedState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val reopenedState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + assertThat(reopenedState.imageEditorState?.localMedia?.uri).isEqualTo(originalLocalMedia.uri) + assertThat(reopenedState.imageEditorState?.edits?.cropRect).isEqualTo(cropRect) + assertThat(reopenedState.imageEditorState?.edits?.rotationDegrees).isEqualTo(90) + } + } + fun `present - sendAsFile attachment is pre-processed without image compression`() = runTest { // Even though the user has enabled "Optimize media quality" globally, picking the file // through the Files picker (sendAsFile = true) must skip compression. Regression test @@ -581,6 +666,121 @@ class AttachmentsPreviewPresenterTest { } } + @Test + fun `present - sending edited media keeps the edited file available until upload starts`() = runTest { + val editedFile = createTempFile(suffix = ".jpeg").toFile().apply { + writeText("edited-media") + } + val sendFileResult = + lambdaRecorder> { file, _, _, _, _ -> + assertThat(file.exists()).isTrue() + Result.success(FakeMediaUploadHandler()) + } + val room = FakeJoinedRoom( + liveTimeline = FakeTimeline().apply { + sendFileLambda = sendFileResult + }, + ) + val presenter = createAttachmentsPreviewPresenter( + room = room, + displayMediaQualitySelectorViews = true, + onDoneListener = OnDoneListener {}, + mediaPreProcessor = FakeMediaPreProcessor().apply { + givenResult( + Result.success( + MediaUploadInfo.AnyFile( + file = editedFile, + fileInfo = FileInfo( + mimetype = MimeTypes.Jpeg, + size = editedFile.length(), + thumbnailInfo = null, + thumbnailSource = null, + ) + ) + ) + ) + }, + attachmentImageEditor = FakeAttachmentImageEditor { + Result.success( + EditedLocalMedia( + localMedia = aLocalMedia(uri = editedFile.toUri()), + file = editedFile, + ) + ) + } + ) + + presenter.test { + val initialState = awaitItem() + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + + editorState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) + val appliedState = consumeItemsUntilPredicate { !it.isApplyingImageEdits && it.imageEditorState == null }.last() + + appliedState.eventSink(AttachmentsPreviewEvent.SendAttachment) + consumeItemsUntilPredicate { it.sendActionState == SendActionState.Done } + + sendFileResult.assertions().isCalledOnce() + } + } + + @Test + fun `present - image with generic mime type and png extension is still editable`() = runTest { + val localMedia = aLocalMedia( + uri = mockMediaUrl, + mediaInfo = anImageMediaInfo().copy( + mimeType = MimeTypes.OctetStream, + filename = "Screenshot.png", + fileExtension = "png", + ), + ) + val presenter = createAttachmentsPreviewPresenter(localMedia = localMedia) + + presenter.test { + val initialState = awaitItem() + assertThat(initialState.canEditImage).isTrue() + + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + assertThat(editorState.imageEditorState).isNotNull() + } + } + + @Test + fun `present - image can still be edited when editor can decode it despite generic media info`() = runTest { + val localMedia = aLocalMedia( + uri = mockMediaUrl, + mediaInfo = anImageMediaInfo().copy( + mimeType = MimeTypes.OctetStream, + filename = "", + fileExtension = "", + ), + ) + val presenter = createAttachmentsPreviewPresenter( + localMedia = localMedia, + attachmentImageEditor = FakeAttachmentImageEditor( + canEditResult = true, + ) { + Result.success( + EditedLocalMedia( + localMedia = localMedia.copy(uri = Uri.parse("file:///tmp/decoded.jpeg")), + file = File("/tmp/decoded.jpeg"), + ) + ) + } + ) + + presenter.test { + val initialState = consumeItemsUntilPredicate { it.canEditImage }.last() + assertThat(initialState.canEditImage).isTrue() + + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + assertThat(editorState.imageEditorState).isNotNull() + } + } + @Test fun `present - sendAsFile video is pre-processed with best fitting preset`() = runTest { val mediaPreProcessor = FakeMediaPreProcessor() @@ -652,6 +852,14 @@ class AttachmentsPreviewPresenterTest { } ), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), + attachmentImageEditor: AttachmentImageEditor = FakeAttachmentImageEditor { + Result.success( + EditedLocalMedia( + localMedia = localMedia.copy(uri = Uri.parse("file:///tmp/default-edited.jpeg")), + file = File("/tmp/default-edited.jpeg"), + ) + ) + }, videoCompressionPresetSelector: VideoCompressionPresetSelector = VideoCompressionPresetSelector(), ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( @@ -669,6 +877,7 @@ class AttachmentsPreviewPresenterTest { }, permalinkBuilder = permalinkBuilder, temporaryUriDeleter = temporaryUriDeleter, + attachmentImageEditor = attachmentImageEditor, sessionCoroutineScope = this, dispatchers = testCoroutineDispatchers(), mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory, @@ -679,6 +888,22 @@ class AttachmentsPreviewPresenterTest { ) } + private class FakeAttachmentImageEditor( + private val canEditResult: Boolean = true, + private val result: () -> Result, + ) : AttachmentImageEditor { + override suspend fun canEdit(localMedia: LocalMedia): Boolean { + return canEditResult + } + + override suspend fun exportEdits( + localMedia: LocalMedia, + edits: AttachmentImageEdits, + ): Result { + return result() + } + } + private val mediaUploadInfo = MediaUploadInfo.AnyFile( File("test"), FileInfo( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt new file mode 100644 index 0000000000..fa9c6367f8 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes +import org.junit.Test + +class AttachmentImageEditModelsTest { + @Test + fun `resize with top handle only updates the top edge`() { + val rect = NormalizedCropRect( + left = 0.2f, + top = 0.2f, + right = 0.8f, + bottom = 0.8f, + ) + + val resized = rect.resize( + dragTarget = CropDragTarget.Top, + deltaX = 0.3f, + deltaY = 0.1f, + ) + + assertThat(resized.left).isEqualTo(rect.left) + assertThat(resized.right).isEqualTo(rect.right) + assertThat(resized.bottom).isEqualTo(rect.bottom) + assertThat(resized.top).isEqualTo(0.3f) + } + + @Test + fun `translate keeps the crop rect inside bounds`() { + val rect = NormalizedCropRect( + left = 0.2f, + top = 0.2f, + right = 0.8f, + bottom = 0.8f, + ) + + val translated = rect.translate( + deltaX = 0.6f, + deltaY = 0.6f, + ) + + assertThat(translated.left).isWithin(0.0001f).of(0.4f) + assertThat(translated.top).isWithin(0.0001f).of(0.4f) + assertThat(translated.right).isWithin(0.0001f).of(1.0f) + assertThat(translated.bottom).isWithin(0.0001f).of(1.0f) + } + + @Test + fun `rotate clockwise normalizes after a full turn`() { + var edits = AttachmentImageEdits() + + repeat(4) { + edits = edits.rotateClockwise() + } + + assertThat(edits.normalizedRotationQuarterTurns).isEqualTo(0) + assertThat(edits.rotationDegrees).isEqualTo(0) + assertThat(edits.hasChanges).isFalse() + } + + @Test + fun `exported mime type preserves png`() { + assertThat(exportedMimeTypeFor(MimeTypes.Png)).isEqualTo(MimeTypes.Png) + } + + @Test + fun `exported mime type normalizes non-png images to jpeg`() { + assertThat(exportedMimeTypeFor("image/heic")).isEqualTo(MimeTypes.Jpeg) + } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt index 83e8a17cf4..09164e36d1 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactory.kt @@ -9,7 +9,9 @@ package io.element.android.libraries.mediaviewer.impl.local import android.content.Context +import android.graphics.BitmapFactory import android.net.Uri +import android.webkit.MimeTypeMap import androidx.core.net.toUri import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding @@ -17,6 +19,7 @@ import io.element.android.libraries.androidutils.file.getFileName import io.element.android.libraries.androidutils.file.getFileSize import io.element.android.libraries.androidutils.file.getMimeType import io.element.android.libraries.androidutils.filesize.FileSizeFormatter +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.core.UserId @@ -85,8 +88,12 @@ class AndroidLocalMediaFactory( waveform: List?, duration: String?, ): LocalMedia { - val resolvedMimeType = mimeType ?: context.getMimeType(uri) ?: MimeTypes.OctetStream val fileName = name ?: context.getFileName(uri) ?: "" + val resolvedMimeType = resolveMimeType( + uri = uri, + mimeType = mimeType, + fileName = fileName, + ) val fileSize = context.getFileSize(uri) val calculatedFormattedFileSize = formattedFileSize ?: fileSizeFormatter.format(fileSize) val fileExtension = fileExtensionExtractor.extractFromName(fileName) @@ -110,4 +117,36 @@ class AndroidLocalMediaFactory( ) ) } + + private fun resolveMimeType( + uri: Uri, + mimeType: String?, + fileName: String, + ): String { + val explicitMimeType = mimeType.takeUnless { it.isNullOrBlank() || it == MimeTypes.OctetStream } + if (explicitMimeType != null) return explicitMimeType + + val resolverMimeType = context.getMimeType(uri).takeUnless { it.isNullOrBlank() || it == MimeTypes.OctetStream } + if (resolverMimeType != null) return resolverMimeType + + val decodedImageMimeType = decodeImageMimeType(uri) + if (decodedImageMimeType != null) return decodedImageMimeType + + val extensionMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + fileExtensionExtractor.extractFromName(fileName) + ) + if (!extensionMimeType.isNullOrBlank()) return extensionMimeType + + return MimeTypes.OctetStream + } + + private fun decodeImageMimeType(uri: Uri): String? { + return tryOrNull { + context.contentResolver.openInputStream(uri)?.use { inputStream -> + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeStream(inputStream, null, options) + options.outMimeType + } + } + } } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt index f01ac1d749..26f9087b39 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -8,7 +8,9 @@ package io.element.android.libraries.mediaviewer.impl.local +import android.graphics.Bitmap import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.androidutils.file.getMimeType import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile @@ -22,9 +24,13 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment +import java.io.File +import java.io.FileOutputStream @RunWith(RobolectricTestRunner::class) class AndroidLocalMediaFactoryTest { + private val context = RuntimeEnvironment.getApplication() + @Test fun `test AndroidLocalMediaFactory`() { val sut = createAndroidLocalMediaFactory() @@ -58,13 +64,34 @@ class AndroidLocalMediaFactoryTest { ) } + @Test + fun `createFromUri detects image mime type from content when picker mime type is generic`() { + val imageFile = File(context.cacheDir, "picked-media").apply { + FileOutputStream(this).use { outputStream -> + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + .compress(Bitmap.CompressFormat.PNG, 100, outputStream) + } + } + + val result = createAndroidLocalMediaFactory().createFromUri( + uri = imageFile.toURI().toString().let(android.net.Uri::parse), + mimeType = MimeTypes.OctetStream, + name = imageFile.name, + formattedFileSize = null, + ) + + assertThat(context.getMimeType(result.uri)).isNull() + assertThat(result.info.mimeType).isEqualTo(MimeTypes.Png) + assertThat(result.info.fileExtension).isEmpty() + } + private fun aMediaFile(): MediaFile { return FakeMediaFile("aPath") } private fun createAndroidLocalMediaFactory(): AndroidLocalMediaFactory { return AndroidLocalMediaFactory( - RuntimeEnvironment.getApplication(), + context, FakeFileSizeFormatter(), FileExtensionExtractorWithoutValidation() ) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png new file mode 100644 index 0000000000..37cc9056e1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:490534398a7061e52f66e3ec46d6212107ee2512ff91c768d6618adb24e858f5 +size 376383 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png new file mode 100644 index 0000000000..cb129ba75a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e02017a71217184dc12c4b24b68e344fd20ca374e90e6073506170dd103e16b +size 375365 From 5277382e6d9faf24174e4f2188cb15783695cf50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 07:57:00 +0200 Subject: [PATCH 223/471] Update dependencyAnalysis to v3.12.0 (#6840) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3d49f2cda1..a328218d79 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,7 +51,7 @@ telephoto = "0.19.0" haze = "1.7.2" # Dependency analysis -dependencyAnalysis = "3.11.0" +dependencyAnalysis = "3.12.0" # DI metro = "1.1.1" From bb2779549e6f94689f9e71be66b835d1616d4e7b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 21 May 2026 18:04:49 +0200 Subject: [PATCH 224/471] Update design and UX Update and add tests Improve preview --- .../preview/AttachmentsPreviewEvent.kt | 3 +- .../preview/AttachmentsPreviewPresenter.kt | 9 +- .../preview/AttachmentsPreviewView.kt | 86 +++-- .../imageeditor/AttachmentImageEditModels.kt | 85 +++-- .../imageeditor/AttachmentImageEditor.kt | 4 +- .../AttachmentImageEditorStateProvider.kt | 61 ++++ .../imageeditor/AttachmentImageEditorView.kt | 323 ++++++++++-------- .../impl/src/main/res/values/localazy.xml | 7 + .../impl/src/main/res/values/temporary.xml | 8 - .../AttachmentsPreviewPresenterTest.kt | 4 +- .../AttachmentImageEditModelsTest.kt | 78 ----- .../imageeditor/AttachmentImageEditsTest.kt | 45 +++ .../DefaultAttachmentImageEditorTest.kt | 24 ++ .../imageeditor/NormalizedCropRectTest.kt | 137 ++++++++ .../src/main/res/values/localazy.xml | 1 + tools/localazy/config.json | 1 + 16 files changed, 575 insertions(+), 301 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt delete mode 100644 features/messages/impl/src/main/res/values/temporary.xml delete mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/DefaultAttachmentImageEditorTest.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt index 1957a8b0f7..a7c64845d1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt @@ -16,8 +16,9 @@ sealed interface AttachmentsPreviewEvent { data object CancelAndClearSendState : AttachmentsPreviewEvent data object OpenImageEditor : AttachmentsPreviewEvent data object CloseImageEditor : AttachmentsPreviewEvent - data object RotateImage : AttachmentsPreviewEvent + data object RotateImageToTheLeft : AttachmentsPreviewEvent data object ApplyImageEdits : AttachmentsPreviewEvent + data object ResetImageEdits : AttachmentsPreviewEvent data class UpdateImageCropRect(val cropRect: NormalizedCropRect) : AttachmentsPreviewEvent data object ClearImageEditError : AttachmentsPreviewEvent } 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 b5cb13e3e3..7e8bfbdc3f 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 @@ -287,10 +287,15 @@ class AttachmentsPreviewPresenter( edits = pendingState.edits.copy(cropRect = event.cropRect) ) } - AttachmentsPreviewEvent.RotateImage -> { + AttachmentsPreviewEvent.RotateImageToTheLeft -> { val pendingState = imageEditorState ?: return imageEditorState = pendingState.copy( - edits = pendingState.edits.rotateClockwise() + edits = pendingState.edits.rotateAntiClockwise() + ) + } + AttachmentsPreviewEvent.ResetImageEdits -> { + imageEditorState = imageEditorState?.copy( + edits = AttachmentImageEdits() ) } AttachmentsPreviewEvent.ApplyImageEdits -> { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 2c51fa4499..1714a8b8dc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -31,11 +31,12 @@ 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.semantics.heading +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview 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.R import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError @@ -56,12 +57,11 @@ import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreviewDark 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.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Switch 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.designsystem.utils.CommonDrawables import io.element.android.libraries.mediaviewer.api.local.LocalMedia @@ -76,6 +76,9 @@ import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +/** + * Ref: https://www.figma.com/design/zftpgS6LjiczobJZ1GUNpt/Updates-to-Media---File-Upload?node-id=51-3514 + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun AttachmentsPreviewView( @@ -110,6 +113,10 @@ fun AttachmentsPreviewView( state.eventSink(AttachmentsPreviewEvent.CloseImageEditor) } + fun postResetImageEditor() { + state.eventSink(AttachmentsPreviewEvent.ResetImageEdits) + } + fun postApplyImageEdits() { state.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) } @@ -128,8 +135,9 @@ fun AttachmentsPreviewView( onCropRectChange = { cropRect -> state.eventSink(AttachmentsPreviewEvent.UpdateImageCropRect(cropRect)) }, - onRotateClick = { state.eventSink(AttachmentsPreviewEvent.RotateImage) }, + onRotateClick = { state.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) }, onCancelClick = ::postCloseImageEditor, + onResetClick = ::postResetImageEditor, onDoneClick = ::postApplyImageEdits, modifier = modifier, ) @@ -140,19 +148,23 @@ fun AttachmentsPreviewView( TopAppBar( navigationIcon = { BackButton( - imageVector = CompoundIcons.Close(), onClick = ::postCancel, ) }, - title = {}, + title = { + Text( + modifier = Modifier.semantics { + heading() + }, + text = stringResource(R.string.screen_media_upload_preview_title), + ) + }, actions = { if (state.canEditImage && canShowEditAction) { - IconButton(onClick = ::postOpenImageEditor) { - Icon( - imageVector = CompoundIcons.Edit(), - contentDescription = stringResource(CommonStrings.action_edit), - ) - } + TextButton( + stringResource(CommonStrings.action_edit), + onClick = ::postOpenImageEditor + ) } } ) @@ -202,32 +214,32 @@ private fun AttachmentSendStateView( ) } else -> when (sendActionState) { - is SendActionState.Sending.Processing -> { - if (sendActionState.displayProgress) { + is SendActionState.Sending.Processing -> { + if (sendActionState.displayProgress) { + ProgressDialog( + type = ProgressDialogType.Indeterminate, + text = stringResource(CommonStrings.common_preparing), + showCancelButton = true, + onDismissRequest = onDismissClick, + ) + } + } + is SendActionState.Sending.Uploading -> { ProgressDialog( type = ProgressDialogType.Indeterminate, - text = stringResource(CommonStrings.common_preparing), + text = stringResource(id = CommonStrings.common_sending), showCancelButton = true, onDismissRequest = onDismissClick, ) } - } - is SendActionState.Sending.Uploading -> { - ProgressDialog( - type = ProgressDialogType.Indeterminate, - text = stringResource(id = CommonStrings.common_sending), - showCancelButton = true, - onDismissRequest = onDismissClick, - ) - } - is SendActionState.Failure -> { - RetryDialog( - content = stringResource(sendAttachmentError(sendActionState.error)), - onDismiss = onDismissClick, - onRetry = onRetryClick - ) - } - else -> Unit + is SendActionState.Failure -> { + RetryDialog( + content = stringResource(sendAttachmentError(sendActionState.error)), + onDismiss = onDismissClick, + onRetry = onRetryClick + ) + } + else -> Unit } } } @@ -291,7 +303,8 @@ private fun AttachmentPreviewContent( private fun ImageOptimizationSelector(state: MediaOptimizationSelectorState) { if (state.displayMediaSelectorViews == true) { Row( - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .niceClickable { state.isImageOptimizationEnabled?.let { value -> state.eventSink(MediaOptimizationSelectorEvent.SelectImageOptimization(!value)) @@ -300,7 +313,9 @@ private fun ImageOptimizationSelector(state: MediaOptimizationSelectorState) { .padding(horizontal = 16.dp, vertical = 16.dp) ) { Text( - modifier = Modifier.weight(1f).align(Alignment.CenterVertically), + modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically), text = stringResource(R.string.screen_media_upload_preview_optimize_image_quality_title), style = ElementTheme.typography.fontBodyLgRegular, ) @@ -326,7 +341,8 @@ private fun VideoPresetSelector( if (state.displayMediaSelectorViews == true && videoPresets != null && state.selectedVideoPreset != null) { Column( - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 16.dp) .niceClickable { state.eventSink(MediaOptimizationSelectorEvent.OpenVideoPresetSelectorDialog) } ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt index 90872df4f2..3143f2462e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.attachments.preview.imageedito import androidx.compose.runtime.Immutable import io.element.android.libraries.mediaviewer.api.local.LocalMedia -private const val DEFAULT_CROP_MARGIN = 0.1f +private const val DEFAULT_CROP_MARGIN = 0f private const val MIN_CROP_SIZE = 0.1f @Immutable @@ -25,7 +25,7 @@ data class AttachmentImageEdits( val rotationQuarterTurns: Int = 0, ) { val normalizedRotationQuarterTurns: Int - get() = (rotationQuarterTurns % 4 + 4) % 4 + get() = rotationQuarterTurns % 4 val rotationDegrees: Int get() = normalizedRotationQuarterTurns * 90 @@ -33,8 +33,17 @@ data class AttachmentImageEdits( val hasChanges: Boolean get() = cropRect != NormalizedCropRect.default() || normalizedRotationQuarterTurns != 0 - fun rotateClockwise(): AttachmentImageEdits { - return copy(rotationQuarterTurns = (normalizedRotationQuarterTurns + 1) % 4) + fun rotateAntiClockwise(): AttachmentImageEdits { + return copy( + rotationQuarterTurns = (normalizedRotationQuarterTurns + 3) % 4, + // Also update the crop rect to keep the same selected area + cropRect = NormalizedCropRect( + left = cropRect.top, + top = 1f - cropRect.right, + right = cropRect.bottom, + bottom = 1f - cropRect.left, + ) + ) } } @@ -60,7 +69,13 @@ data class NormalizedCropRect( val height: Float get() = bottom - top - fun translate(deltaX: Float, deltaY: Float): NormalizedCropRect { + fun applyChange(dragTarget: CropDragTarget, deltaX: Float, deltaY: Float): NormalizedCropRect = when (dragTarget) { + is CropDragTarget.Move -> translate(deltaX, deltaY) + is CropDragTarget.Corner -> dragWithCorner(dragTarget, deltaX, deltaY) + is CropDragTarget.Edge -> dragWithEdge(dragTarget, deltaX, deltaY) + } + + private fun translate(deltaX: Float, deltaY: Float): NormalizedCropRect { val clampedLeft = (left + deltaX).coerceIn(0f, 1f - width) val clampedTop = (top + deltaY).coerceIn(0f, 1f - height) return copy( @@ -71,34 +86,36 @@ data class NormalizedCropRect( ) } - fun resize(dragTarget: CropDragTarget, deltaX: Float, deltaY: Float): NormalizedCropRect = when (dragTarget) { - CropDragTarget.Move -> translate(deltaX, deltaY) - CropDragTarget.TopLeft -> copy( + private fun dragWithCorner(dragTarget: CropDragTarget.Corner, deltaX: Float, deltaY: Float) = when (dragTarget) { + CropDragTarget.Corner.TopLeft -> copy( left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), ) - CropDragTarget.Top -> copy( - top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), - ) - CropDragTarget.TopRight -> copy( + CropDragTarget.Corner.TopRight -> copy( right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), ) - CropDragTarget.Right -> copy( - right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), - ) - CropDragTarget.BottomRight -> copy( + CropDragTarget.Corner.BottomRight -> copy( right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), ) - CropDragTarget.Bottom -> copy( - bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), - ) - CropDragTarget.BottomLeft -> copy( + CropDragTarget.Corner.BottomLeft -> copy( left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), ) - CropDragTarget.Left -> copy( + } + + private fun dragWithEdge(dragTarget: CropDragTarget.Edge, deltaX: Float, deltaY: Float) = when (dragTarget) { + CropDragTarget.Edge.Top -> copy( + top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), + ) + CropDragTarget.Edge.Right -> copy( + right = (right + deltaX).coerceIn(left + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.Edge.Bottom -> copy( + bottom = (bottom + deltaY).coerceIn(top + MIN_CROP_SIZE, 1f), + ) + CropDragTarget.Edge.Left -> copy( left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), ) } @@ -113,14 +130,20 @@ data class NormalizedCropRect( } } -enum class CropDragTarget { - Move, - TopLeft, - Top, - TopRight, - Right, - BottomRight, - Bottom, - BottomLeft, - Left, +sealed interface CropDragTarget { + data object Move : CropDragTarget + + sealed interface Corner : CropDragTarget { + data object TopLeft : Corner + data object TopRight : Corner + data object BottomRight : Corner + data object BottomLeft : Corner + } + + sealed interface Edge : CropDragTarget { + data object Top : Edge + data object Right : Edge + data object Bottom : Edge + data object Left : Edge + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt index f003c19084..cb66802184 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt @@ -174,12 +174,12 @@ private fun NormalizedCropRect.toPixelRect(imageWidth: Int, imageHeight: Int): P } private fun compressFormat(mimeType: String) = when (mimeType) { - "image/png" -> Bitmap.CompressFormat.PNG + MimeTypes.Png -> Bitmap.CompressFormat.PNG else -> Bitmap.CompressFormat.JPEG } private fun compressFileExtension(mimeType: String) = when (mimeType) { - "image/png" -> "png" + MimeTypes.Png -> "png" else -> "jpeg" } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt new file mode 100644 index 0000000000..d6f4a835bc --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.core.net.toUri +import io.element.android.libraries.mediaviewer.api.anImageMediaInfo +import io.element.android.libraries.mediaviewer.api.local.LocalMedia + +open class AttachmentImageEditorStateProvider : PreviewParameterProvider { + private val caterpillarCrop = NormalizedCropRect( + left = 0.3f, + top = 0.3f, + right = 0.8f, + bottom = 0.75f, + ) + + override val values: Sequence + get() = sequenceOf( + anAttachmentImageEditorState( + edits = AttachmentImageEdits( + // Cheat a bit so that the crop match the sample image size (1024 * 682) + cropRect = 0.17f.let { correction -> + NormalizedCropRect( + left = 0f, + top = correction, + right = 1f, + bottom = 1 - correction, + ) + }, + ), + ), + anAttachmentImageEditorState( + edits = AttachmentImageEdits( + cropRect = caterpillarCrop, + ), + ), + anAttachmentImageEditorState( + edits = AttachmentImageEdits( + cropRect = caterpillarCrop, + ).rotateAntiClockwise(), + ), + ) +} + +private fun anAttachmentImageEditorState( + localMedia: LocalMedia = LocalMedia( + uri = "file://preview-image".toUri(), + info = anImageMediaInfo(), + ), + edits: AttachmentImageEdits = AttachmentImageEdits(), +) = + AttachmentImageEditorState( + localMedia = localMedia, + edits = edits, + ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt index bf3958e83f..7f8a42322a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt @@ -10,20 +10,19 @@ package io.element.android.features.messages.impl.attachments.preview.imageedito import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints 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.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable @@ -37,6 +36,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput @@ -48,18 +48,20 @@ import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import androidx.core.net.toUri import coil3.compose.AsyncImage import coil3.compose.AsyncImagePainter import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.preview.ElementPreviewDark 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 @@ -67,27 +69,29 @@ 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.designsystem.utils.CommonDrawables -import io.element.android.libraries.mediaviewer.api.anImageMediaInfo -import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.ui.strings.CommonStrings +/** + * Ref: https://www.figma.com/design/zftpgS6LjiczobJZ1GUNpt/Updates-to-Media---File-Upload?node-id=51-3539 + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun AttachmentImageEditorView( state: AttachmentImageEditorState, onCropRectChange: (NormalizedCropRect) -> Unit, onRotateClick: () -> Unit, + onResetClick: () -> Unit, onCancelClick: () -> Unit, onDoneClick: () -> Unit, modifier: Modifier = Modifier, ) { - val rotateContentDescription = stringResource(R.string.screen_media_upload_preview_rotate) + val rotateContentDescription = stringResource(R.string.screen_image_edition_a11y_rotate_to_the_left) val rotationStateDescription = pluralStringResource( - R.plurals.a11y_media_upload_preview_rotation_degrees, + R.plurals.screen_image_edition_a11y_rotation_state, state.edits.rotationDegrees, state.edits.rotationDegrees, ) - val rotateButtonBackground = ElementTheme.colors.bgSubtlePrimary + val rotateButtonBackground = ElementTheme.colors.bgCanvasDefault Scaffold( modifier = modifier.fillMaxSize(), @@ -99,7 +103,14 @@ fun AttachmentImageEditorView( onClick = onCancelClick, ) }, - title = {}, + title = { + Text( + modifier = Modifier.semantics { + heading() + }, + text = stringResource(R.string.screen_image_edition_title), + ) + }, ) } ) { paddingValues -> @@ -118,79 +129,59 @@ fun AttachmentImageEditorView( onCropRectChange = onCropRectChange, ) } - - Box( + Row( modifier = Modifier - .fillMaxWidth() - .height(132.dp) - .background(ElementTheme.colors.bgCanvasDefault) + .align(Alignment.CenterHorizontally) + .widthIn(max = 360.dp) + .navigationBarsPadding() + .padding(start = 20.dp, top = 18.dp, end = 20.dp, bottom = 18.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, ) { - Row( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) - .navigationBarsPadding() - .padding(start = 20.dp, top = 18.dp, end = 20.dp, bottom = 10.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.CenterStart, ) { - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.CenterStart, - ) { - TextButton( - text = stringResource(CommonStrings.action_cancel), - onClick = onCancelClick, - ) - } - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.Center, - ) { - IconButton( - onClick = onRotateClick, - modifier = Modifier - .size(72.dp) - .background( - color = rotateButtonBackground, - shape = CircleShape, - ) - .clearAndSetSemantics { - contentDescription = rotateContentDescription - stateDescription = rotationStateDescription - } - ) { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Spacer(modifier = Modifier.height(2.dp)) - Icon( - modifier = Modifier - .size(22.dp), - imageVector = CompoundIcons.RotateRight(), - contentDescription = null, - ) - Spacer(modifier = Modifier.height(3.dp)) - Text( - text = "${state.edits.rotationDegrees}°", - style = ElementTheme.typography.fontBodyXsMedium, - color = ElementTheme.colors.textSecondary, - ) + TextButton( + text = stringResource(CommonStrings.action_reset), + destructive = true, + onClick = onResetClick, + ) + } + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center, + ) { + IconButton( + onClick = onRotateClick, + modifier = Modifier + .background( + color = rotateButtonBackground, + shape = CircleShape, + ) + .border(1.dp, ElementTheme.colors.borderInteractiveSecondary, CircleShape) + .clearAndSetSemantics { + contentDescription = rotateContentDescription + stateDescription = rotationStateDescription } - } - } - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.CenterEnd, ) { - TextButton( - text = stringResource(CommonStrings.action_done), - onClick = onDoneClick, + Icon( + modifier = Modifier + .size(22.dp), + imageVector = CompoundIcons.RotateLeft(), + contentDescription = null, ) } } + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.CenterEnd, + ) { + TextButton( + text = stringResource(CommonStrings.action_done), + onClick = onDoneClick, + ) + } } } } @@ -207,6 +198,7 @@ private fun CropEditorCanvas( BoxWithConstraints( modifier = Modifier .fillMaxSize() + .padding(20.dp), ) { val displayedSize = remember(maxWidth, maxHeight, imageSize, rotationQuarterTurns) { val sourceWidth = imageSize.width.takeIf { it > 0 } ?: 1 @@ -287,9 +279,9 @@ private fun CropOverlay( ) { var dragTarget by remember { mutableStateOf(null) } val latestCropRect by rememberUpdatedState(cropRect) - val borderColor = ElementTheme.colors.textPrimary - val guideColor = ElementTheme.colors.textSecondary - + val borderColor = ElementTheme.colors.iconPrimary + val guideColor = ElementTheme.colors.iconPrimary + val drawGuidelines = dragTarget == CropDragTarget.Move Canvas( modifier = Modifier .fillMaxSize() @@ -313,7 +305,7 @@ private fun CropOverlay( val activeTarget = dragTarget ?: return@detectDragGestures change.consume() onCropRectChange( - latestCropRect.resize( + latestCropRect.applyChange( dragTarget = activeTarget, deltaX = dragAmount.x / size.width.toFloat(), deltaY = dragAmount.y / size.height.toFloat(), @@ -329,80 +321,120 @@ private fun CropOverlay( // Hardcoded black: the crop overlay must always darken the image regardless of theme. // No semantic token exists for this use case in the Compound design system. val overlayColor = Color.Black.copy(alpha = 0.48f) - + // Overlay above the crop area drawRect( color = overlayColor, topLeft = Offset.Zero, size = Size(width = size.width, height = cropTop), ) + // Overlay on the left of the crop area drawRect( color = overlayColor, topLeft = Offset(0f, cropTop), size = Size(width = cropLeft, height = cropBottom - cropTop), ) + // Overlay on the right of the crop area drawRect( color = overlayColor, topLeft = Offset(cropRight, cropTop), size = Size(width = size.width - cropRight, height = cropBottom - cropTop), ) + // Overlay below the crop area drawRect( color = overlayColor, topLeft = Offset(0f, cropBottom), size = Size(width = size.width, height = size.height - cropBottom), ) - + // Main frame of the crop area drawRect( color = borderColor, topLeft = Offset(cropLeft, cropTop), size = Size(width = cropRight - cropLeft, height = cropBottom - cropTop), - style = Stroke(width = 2.dp.toPx()), + style = Stroke(width = 1.dp.toPx()), ) - - val thirdWidth = (cropRight - cropLeft) / 3f - val thirdHeight = (cropBottom - cropTop) / 3f - repeat(2) { index -> - val offsetX = cropLeft + thirdWidth * (index + 1) - val offsetY = cropTop + thirdHeight * (index + 1) - drawLine( - color = guideColor, - start = Offset(offsetX, cropTop), - end = Offset(offsetX, cropBottom), - strokeWidth = 1.dp.toPx(), - ) - drawLine( - color = guideColor, - start = Offset(cropLeft, offsetY), - end = Offset(cropRight, offsetY), - strokeWidth = 1.dp.toPx(), - ) + // Guide lines dividing the crop area into 9 equal parts + if (drawGuidelines) { + val thirdWidth = (cropRight - cropLeft) / 3f + val thirdHeight = (cropBottom - cropTop) / 3f + (1..2).forEach { index -> + val offsetX = cropLeft + thirdWidth * index + val offsetY = cropTop + thirdHeight * index + // Vertical guide line + drawLine( + color = guideColor, + start = Offset(offsetX, cropTop), + end = Offset(offsetX, cropBottom), + strokeWidth = 1.dp.toPx(), + ) + // Horizontal guide line + drawLine( + color = guideColor, + start = Offset(cropLeft, offsetY), + end = Offset(cropRight, offsetY), + strokeWidth = 1.dp.toPx(), + ) + } } - - val handleLength = 16.dp.toPx() + // Corner handles + val handleLength = 18.dp.toPx() + val handleOffset = 2.dp.toPx() + // Top left corner + drawCornerHandle( + x = cropLeft - handleOffset, + y = cropTop - handleOffset, + handleLength = handleLength, + color = borderColor, + position = CropDragTarget.Corner.TopLeft, + ) + // Top right corner + drawCornerHandle( + x = cropRight + handleOffset, + y = cropTop - handleOffset, + handleLength = handleLength, + color = borderColor, + position = CropDragTarget.Corner.TopRight, + ) + // Bottom left corner + drawCornerHandle( + x = cropLeft - handleOffset, + y = cropBottom + handleOffset, + handleLength = handleLength, + color = borderColor, + position = CropDragTarget.Corner.BottomLeft, + ) + // Bottom right corner + drawCornerHandle( + x = cropRight + handleOffset, + y = cropBottom + handleOffset, + handleLength = handleLength, + color = borderColor, + position = CropDragTarget.Corner.BottomRight, + ) val handleColor = borderColor - drawCornerHandle(cropLeft, cropTop, handleLength, handleColor, true, true) - drawCornerHandle(cropRight, cropTop, handleLength, handleColor, false, true) - drawCornerHandle(cropLeft, cropBottom, handleLength, handleColor, true, false) - drawCornerHandle(cropRight, cropBottom, handleLength, handleColor, false, false) + // Top handle drawEdgeHandle( - center = Offset((cropLeft + cropRight) / 2f, cropTop), + center = Offset((cropLeft + cropRight) / 2f, cropTop - handleOffset), horizontal = true, handleLength = handleLength, color = handleColor, ) + // Right handle drawEdgeHandle( - center = Offset(cropRight, (cropTop + cropBottom) / 2f), + center = Offset(cropRight + handleOffset, (cropTop + cropBottom) / 2f), horizontal = false, handleLength = handleLength, color = handleColor, ) + // Bottom handle drawEdgeHandle( - center = Offset((cropLeft + cropRight) / 2f, cropBottom), + center = Offset((cropLeft + cropRight) / 2f, cropBottom + handleOffset), horizontal = true, handleLength = handleLength, color = handleColor, ) + // Left handle drawEdgeHandle( - center = Offset(cropLeft, (cropTop + cropBottom) / 2f), + center = Offset(cropLeft - handleOffset, (cropTop + cropBottom) / 2f), horizontal = false, handleLength = handleLength, color = handleColor, @@ -430,14 +462,14 @@ private fun detectDragTarget( handleTouchRadius: Float, ): CropDragTarget? { val corners = mapOf( - CropDragTarget.TopLeft to Offset(cropRect.left * canvasSize.width, cropRect.top * canvasSize.height), - CropDragTarget.Top to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.top * canvasSize.height), - CropDragTarget.TopRight to Offset(cropRect.right * canvasSize.width, cropRect.top * canvasSize.height), - CropDragTarget.Right to Offset(cropRect.right * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), - CropDragTarget.BottomRight to Offset(cropRect.right * canvasSize.width, cropRect.bottom * canvasSize.height), - CropDragTarget.Bottom to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.bottom * canvasSize.height), - CropDragTarget.BottomLeft to Offset(cropRect.left * canvasSize.width, cropRect.bottom * canvasSize.height), - CropDragTarget.Left to Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), + CropDragTarget.Corner.TopLeft to Offset(cropRect.left * canvasSize.width, cropRect.top * canvasSize.height), + CropDragTarget.Edge.Top to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.top * canvasSize.height), + CropDragTarget.Corner.TopRight to Offset(cropRect.right * canvasSize.width, cropRect.top * canvasSize.height), + CropDragTarget.Edge.Right to Offset(cropRect.right * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), + CropDragTarget.Corner.BottomRight to Offset(cropRect.right * canvasSize.width, cropRect.bottom * canvasSize.height), + CropDragTarget.Edge.Bottom to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.bottom * canvasSize.height), + CropDragTarget.Corner.BottomLeft to Offset(cropRect.left * canvasSize.width, cropRect.bottom * canvasSize.height), + CropDragTarget.Edge.Left to Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), ) corners.forEach { (target, corner) -> if ((corner - touchPoint).getDistance() <= handleTouchRadius) { @@ -455,31 +487,40 @@ private fun detectDragTarget( } } -private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawCornerHandle( +// x and y are the coordinates of the corner +private fun DrawScope.drawCornerHandle( x: Float, y: Float, handleLength: Float, color: Color, - isLeft: Boolean, - isTop: Boolean, + position: CropDragTarget.Corner, ) { - val horizontalEndX = if (isLeft) x + handleLength else x - handleLength - val verticalEndY = if (isTop) y + handleLength else y - handleLength + val strokeWidth = 4.dp.toPx() + val correction = strokeWidth / 2 + val horizontalCorrection = if (position.isLeft()) -correction else correction + val horizontalEndX = if (position.isLeft()) x + handleLength else x - handleLength + val verticalEndY = if (position.isTop()) y + handleLength else y - handleLength + val verticalCorrection = if (position.isTop()) -correction else correction + // Horizontal line drawLine( color = color, - start = Offset(x, y), - end = Offset(horizontalEndX, y), - strokeWidth = 3.dp.toPx(), + start = Offset(x + horizontalCorrection, y), + end = Offset(horizontalEndX + horizontalCorrection, y), + strokeWidth = strokeWidth, ) + // Vertical line drawLine( color = color, - start = Offset(x, y), - end = Offset(x, verticalEndY), - strokeWidth = 3.dp.toPx(), + start = Offset(x, y + verticalCorrection), + end = Offset(x, verticalEndY + verticalCorrection), + strokeWidth = strokeWidth, ) } -private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawEdgeHandle( +private fun CropDragTarget.Corner.isLeft() = this == CropDragTarget.Corner.TopLeft || this == CropDragTarget.Corner.BottomLeft +private fun CropDragTarget.Corner.isTop() = this == CropDragTarget.Corner.TopLeft || this == CropDragTarget.Corner.TopRight + +private fun DrawScope.drawEdgeHandle( center: Offset, horizontal: Boolean, handleLength: Float, @@ -499,23 +540,21 @@ private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawEdgeHandle( color = color, start = start, end = end, - strokeWidth = 3.dp.toPx(), + strokeWidth = 4.dp.toPx(), ) } -@PreviewsDayNight +// Only preview in dark, dark theme is forced on the Node. +@Preview @Composable -internal fun AttachmentImageEditorViewPreview() = ElementPreview { +internal fun AttachmentImageEditorViewPreview( + @PreviewParameter(AttachmentImageEditorStateProvider::class) state: AttachmentImageEditorState, +) = ElementPreviewDark { AttachmentImageEditorView( - state = AttachmentImageEditorState( - localMedia = LocalMedia( - uri = "file://preview-image".toUri(), - info = anImageMediaInfo(), - ), - edits = AttachmentImageEdits(), - ), + state = state, onCropRectChange = {}, onRotateClick = {}, + onResetClick = {}, onCancelClick = {}, onDoneClick = {}, ) diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index f5629f5e2d..908646b048 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -16,6 +16,12 @@ "Travel & Places" "Recent emojis" "Symbols" + "Rotate the image to the left" + + "%1$d degree" + "%1$d degrees" + + "Edit photo" "Captions might not be visible to people using older apps." "Tap to change the video upload quality" "The file could not be uploaded." @@ -26,6 +32,7 @@ "Item %1$d of %2$d" "Optimise image quality" "Processing…" + "Add media" "Block user" "Check if you want to hide all current and future messages from this user" "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." diff --git a/features/messages/impl/src/main/res/values/temporary.xml b/features/messages/impl/src/main/res/values/temporary.xml deleted file mode 100644 index f0050224a9..0000000000 --- a/features/messages/impl/src/main/res/values/temporary.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Rotate - - %1$d degree - %1$d degrees - - 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 5cbfd331c1..962f800764 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 @@ -577,7 +577,7 @@ class AttachmentsPreviewPresenterTest { val editorState = awaitItem() assertThat(editorState.imageEditorState).isNotNull() - editorState.eventSink(AttachmentsPreviewEvent.RotateImage) + editorState.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) val rotatedState = awaitItem() assertThat(rotatedState.imageEditorState?.edits?.rotationQuarterTurns).isEqualTo(1) @@ -621,7 +621,7 @@ class AttachmentsPreviewPresenterTest { editorState.eventSink(AttachmentsPreviewEvent.UpdateImageCropRect(cropRect)) val croppedState = awaitItem() - croppedState.eventSink(AttachmentsPreviewEvent.RotateImage) + croppedState.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) val rotatedState = awaitItem() rotatedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt deleted file mode 100644 index fa9c6367f8..0000000000 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModelsTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.messages.impl.attachments.preview.imageeditor - -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.core.mimetype.MimeTypes -import org.junit.Test - -class AttachmentImageEditModelsTest { - @Test - fun `resize with top handle only updates the top edge`() { - val rect = NormalizedCropRect( - left = 0.2f, - top = 0.2f, - right = 0.8f, - bottom = 0.8f, - ) - - val resized = rect.resize( - dragTarget = CropDragTarget.Top, - deltaX = 0.3f, - deltaY = 0.1f, - ) - - assertThat(resized.left).isEqualTo(rect.left) - assertThat(resized.right).isEqualTo(rect.right) - assertThat(resized.bottom).isEqualTo(rect.bottom) - assertThat(resized.top).isEqualTo(0.3f) - } - - @Test - fun `translate keeps the crop rect inside bounds`() { - val rect = NormalizedCropRect( - left = 0.2f, - top = 0.2f, - right = 0.8f, - bottom = 0.8f, - ) - - val translated = rect.translate( - deltaX = 0.6f, - deltaY = 0.6f, - ) - - assertThat(translated.left).isWithin(0.0001f).of(0.4f) - assertThat(translated.top).isWithin(0.0001f).of(0.4f) - assertThat(translated.right).isWithin(0.0001f).of(1.0f) - assertThat(translated.bottom).isWithin(0.0001f).of(1.0f) - } - - @Test - fun `rotate clockwise normalizes after a full turn`() { - var edits = AttachmentImageEdits() - - repeat(4) { - edits = edits.rotateClockwise() - } - - assertThat(edits.normalizedRotationQuarterTurns).isEqualTo(0) - assertThat(edits.rotationDegrees).isEqualTo(0) - assertThat(edits.hasChanges).isFalse() - } - - @Test - fun `exported mime type preserves png`() { - assertThat(exportedMimeTypeFor(MimeTypes.Png)).isEqualTo(MimeTypes.Png) - } - - @Test - fun `exported mime type normalizes non-png images to jpeg`() { - assertThat(exportedMimeTypeFor("image/heic")).isEqualTo(MimeTypes.Jpeg) - } -} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt new file mode 100644 index 0000000000..43c893dd05 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class AttachmentImageEditsTest { + @Test + fun `rotate normalizes after a full turn`() { + var edits = AttachmentImageEdits() + repeat(4) { + edits = edits.rotateAntiClockwise() + } + assertThat(edits.normalizedRotationQuarterTurns).isEqualTo(0) + assertThat(edits.rotationDegrees).isEqualTo(0) + assertThat(edits.hasChanges).isFalse() + } + + @Test + fun `rotate updates rotation and crop`() { + val sut = AttachmentImageEdits( + cropRect = NormalizedCropRect( + left = 0.2f, + top = 0.3f, + right = 0.8f, + bottom = 0.9f, + ), + rotationQuarterTurns = 0, + ) + val result = sut.rotateAntiClockwise() + assertThat(result.normalizedRotationQuarterTurns).isEqualTo(3) + assertThat(result.rotationDegrees).isEqualTo(270) + assertThat(result.cropRect.left).isWithin(0.0001f).of(0.3f) + assertThat(result.cropRect.top).isWithin(0.0001f).of(0.2f) + assertThat(result.cropRect.right).isWithin(0.0001f).of(0.9f) + assertThat(result.cropRect.bottom).isWithin(0.0001f).of(0.8f) + assertThat(result.hasChanges).isTrue() + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/DefaultAttachmentImageEditorTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/DefaultAttachmentImageEditorTest.kt new file mode 100644 index 0000000000..f9db4f35f9 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/DefaultAttachmentImageEditorTest.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.mimetype.MimeTypes +import org.junit.Test + +class DefaultAttachmentImageEditorTest { + @Test + fun `exported mime type preserves png`() { + assertThat(exportedMimeTypeFor(MimeTypes.Png)).isEqualTo(MimeTypes.Png) + } + + @Test + fun `exported mime type normalizes non-png images to jpeg`() { + assertThat(exportedMimeTypeFor("image/heic")).isEqualTo(MimeTypes.Jpeg) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt new file mode 100644 index 0000000000..b51f10727c --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.attachments.preview.imageeditor + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class NormalizedCropRectTest { + private val rect = NormalizedCropRect( + left = 0.1f, + top = 0.2f, + right = 0.7f, + bottom = 0.8f, + ) + + @Test + fun `applyChange with top handle only updates the top edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Edge.Top, + deltaX = 0.3f, + deltaY = 0.1f, + ) + assertThat(result.left).isEqualTo(rect.left) + assertThat(result.right).isEqualTo(rect.right) + assertThat(result.bottom).isEqualTo(rect.bottom) + assertThat(result.top).isWithin(0.0001f).of(0.3f) + } + + @Test + fun `applyChange with left handle only updates the left edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Edge.Left, + deltaX = 0.1f, + deltaY = 0.3f, + ) + assertThat(result.top).isEqualTo(rect.top) + assertThat(result.right).isEqualTo(rect.right) + assertThat(result.bottom).isEqualTo(rect.bottom) + assertThat(result.left).isWithin(0.0001f).of(0.2f) + } + + @Test + fun `applyChange with right handle only updates the right edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Edge.Right, + deltaX = -0.1f, + deltaY = 0.3f, + ) + assertThat(result.top).isEqualTo(rect.top) + assertThat(result.left).isEqualTo(rect.left) + assertThat(result.bottom).isEqualTo(rect.bottom) + assertThat(result.right).isWithin(0.0001f).of(0.6f) + } + + @Test + fun `applyChange with bottom handle target only updates the bottem edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Edge.Bottom, + deltaX = -0.1f, + deltaY = -0.3f, + ) + assertThat(result.top).isEqualTo(rect.top) + assertThat(result.left).isEqualTo(rect.left) + assertThat(result.right).isEqualTo(rect.right) + assertThat(result.bottom).isWithin(0.0001f).of(0.5f) + } + + @Test + fun `applyChange with top left handle updates the top and left bottem edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Corner.TopLeft, + deltaX = 0.1f, + deltaY = 0.1f, + ) + assertThat(result.top).isWithin(0.0001f).of(0.3f) + assertThat(result.left).isWithin(0.0001f).of(0.2f) + assertThat(result.right).isEqualTo(rect.right) + assertThat(result.bottom).isEqualTo(rect.bottom) + } + + @Test + fun `applyChange with top right handle updates the top and right bottem edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Corner.TopRight, + deltaX = -0.1f, + deltaY = 0.1f, + ) + assertThat(result.top).isWithin(0.0001f).of(0.3f) + assertThat(result.right).isWithin(0.0001f).of(0.6f) + assertThat(result.left).isEqualTo(rect.left) + assertThat(result.bottom).isEqualTo(rect.bottom) + } + + @Test + fun `applyChange with bottom left handle updates the bottom and left bottem edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Corner.BottomLeft, + deltaX = 0.1f, + deltaY = -0.1f, + ) + assertThat(result.bottom).isWithin(0.0001f).of(0.7f) + assertThat(result.left).isWithin(0.0001f).of(0.2f) + assertThat(result.top).isEqualTo(rect.top) + assertThat(result.right).isEqualTo(rect.right) + } + + @Test + fun `applyChange with bottom right handle updates the bottom and right bottem edge`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Corner.BottomRight, + deltaX = -0.1f, + deltaY = -0.1f, + ) + assertThat(result.bottom).isWithin(0.0001f).of(0.7f) + assertThat(result.right).isWithin(0.0001f).of(0.6f) + assertThat(result.top).isEqualTo(rect.top) + assertThat(result.left).isEqualTo(rect.left) + } + + @Test + fun `translate keeps the crop rect inside bounds`() { + val result = rect.applyChange( + dragTarget = CropDragTarget.Move, + deltaX = 0.6f, + deltaY = 0.6f, + ) + assertThat(result.left).isWithin(0.0001f).of(0.4f) + assertThat(result.top).isWithin(0.0001f).of(0.4f) + assertThat(result.right).isWithin(0.0001f).of(1.0f) + assertThat(result.bottom).isWithin(0.0001f).of(1.0f) + } +} diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index e89d45b228..5608486d0e 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -18,6 +18,7 @@ "Info" "Join call" "Jump to bottom" + "Jump to unread" "Move the map to my location" "Mentions only" "Muted" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index cb9c2ecb0d..9936341cee 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -273,6 +273,7 @@ "screen_room_timeline.*", "screen\\.room_timeline.*", "screen_room_typing.*", + "screen\\.image_edition\\..*", "screen\\.media_upload.*" ] }, From aa92bb61c1a4d0aae94d05147481c361a68d4aa3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 22 May 2026 08:45:58 +0000 Subject: [PATCH 225/471] Update screenshots --- ...nts.preview.imageeditor_AttachmentImageEditorView_0_en.png | 3 +++ ...nts.preview.imageeditor_AttachmentImageEditorView_1_en.png | 3 +++ ...nts.preview.imageeditor_AttachmentImageEditorView_2_en.png | 3 +++ ...preview.imageeditor_AttachmentImageEditorView_Day_0_en.png | 3 --- ...eview.imageeditor_AttachmentImageEditorView_Night_0_en.png | 3 --- ...s.impl.attachments.preview_AttachmentsPreviewView_0_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_1_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_2_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_3_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_4_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_5_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_6_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_7_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_8_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_9_en.png | 3 +++ 15 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png new file mode 100644 index 0000000000..3bd0c2ba54 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51f0b3f7e4bb16728f21055de37b7b2780fd2a1fc65b6bd4564334daeab20763 +size 329042 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png new file mode 100644 index 0000000000..bf0a11f093 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5febc6580e4f0bda75a27445157ace7f1acb620c17cba55ca5d2a9330743c1de +size 283397 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png new file mode 100644 index 0000000000..553c54a63d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d99c470fd5134e0a84b284ed32f4c93de01561e630ef32d7c22a4b476bb871b1 +size 277852 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png deleted file mode 100644 index 37cc9056e1..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:490534398a7061e52f66e3ec46d6212107ee2512ff91c768d6618adb24e858f5 -size 376383 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png deleted file mode 100644 index cb129ba75a..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6e02017a71217184dc12c4b24b68e344fd20ca374e90e6073506170dd103e16b -size 375365 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png index 586bf84274..ccca24ef6a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e3d682c4866bd5c519ab88d08290708929af805438a0bd093200cddcbd41b2 -size 399376 +oid sha256:a14113653096095323ecb94cb3dd694985717b8585c8cfc6b7a45cf7a2483a79 +size 402427 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png index 27d8600ab5..c511e0e162 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02bb9e9de3b0ef480cedbed50483bdd3a899497ecc40ead72491106c6f6b6611 -size 399030 +oid sha256:95c64e1e7055dd048b88f0e19a57fe696ee93505d74f96bc4f1915fcce769d7d +size 402072 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en.png index f1cde998de..f229a2a82e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:947ccb947f4a961ff7d17b936f2c866d66fea0361879d51ad4c65d18465c1a9f -size 59226 +oid sha256:4ee2ee54c694a45316bbdcfba7038391fe808f5292356748e9e66f46135a9ae8 +size 61457 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png index 586bf84274..ccca24ef6a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e3d682c4866bd5c519ab88d08290708929af805438a0bd093200cddcbd41b2 -size 399376 +oid sha256:a14113653096095323ecb94cb3dd694985717b8585c8cfc6b7a45cf7a2483a79 +size 402427 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en.png index dd5a1e333c..09e08632b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d9e221ec2f4ee764967e94c32f52b1615b25dec8fc7697dd5bcd01fc4da8d69 -size 59098 +oid sha256:44c7b0e86781ff6112c3be5b575b548c5ae6e9530f6f27a070ebb008d606a20c +size 61326 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png index d695d41c94..f8fbf8c1a1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:547bf4e3bec05c219f5a72cfd8d506eb7c39429970cced5c2b8f2999ae390265 -size 86149 +oid sha256:ccedfe061af8a77da7ddf6b20d23899bfff986fd6c74b87480b588e148ddd8de +size 89013 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png index 4a6e1cd828..fbff934ed8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d326595038160376db620a07a180de7af37ebfc76d4927ed1176ef6f4370aab -size 72700 +oid sha256:f52a267ee2aa300185191aa3e610787c58d7222bc8e4a7570879d4c5fd37133d +size 328936 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png index fa4beb6dfb..5f90adbd8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e8bcb6fdffb2d8673e4ee4e16e21672ffe99e717c48fd35b8411a8aa0530e9 -size 405064 +oid sha256:1feeb32f9dce486c54db41727d9d9801f606353008945988331c2ce391dec451 +size 75364 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png index d789e92297..d7cdc540f7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3caecb171d3095ef5c3593c6289b2d99e6cd6a6c635d822ad24061e502bdeb2e -size 82790 +oid sha256:2bfda29fcaade9508e0d742b0de7ac230d41a358e98755e4b08f1ed7d5affe30 +size 408106 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png new file mode 100644 index 0000000000..f06abb6c7b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fdc4e2d82297927d2062586398fda3f3b7f6febfde8888a062229caf713ff54 +size 85154 From a158da1d18a421e5feb95af159318cb45916ae75 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 12:13:09 +0200 Subject: [PATCH 226/471] For quality issue and improve preview --- .../preview/AttachmentsPreviewPresenter.kt | 1 + .../AttachmentsPreviewStateProvider.kt | 4 ++-- .../imageeditor/AttachmentImageEditModels.kt | 2 ++ .../AttachmentImageEditorStateProvider.kt | 19 +++++++++++++------ .../imageeditor/AttachmentImageEditorView.kt | 8 +++++--- 5 files changed, 23 insertions(+), 11 deletions(-) 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 7e8bfbdc3f..78dd6c2e82 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 @@ -133,6 +133,7 @@ class AttachmentsPreviewPresenter( // If the media optimization selector is not displayed, we can pre-process the media // to prepare it for sending. This is done to avoid blocking the UI thread when the // user clicks on the send button. + @Suppress("ComplexCondition") if (mediaOptimizationSelectorState.displayMediaSelectorViews == false && preprocessMediaJob == null && imageEditorState == null && 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 ced90550c3..a2df440a12 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,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEditorState -import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits +import io.element.android.features.messages.impl.attachments.preview.imageeditor.anAttachmentImageEditorState import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation import io.element.android.libraries.architecture.AsyncData @@ -45,7 +45,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider Unit, ) { var dragTarget by remember { mutableStateOf(null) } val latestCropRect by rememberUpdatedState(cropRect) val borderColor = ElementTheme.colors.iconPrimary val guideColor = ElementTheme.colors.iconPrimary - val drawGuidelines = dragTarget == CropDragTarget.Move + val drawGuidelines = dragTarget == CropDragTarget.Move || forceDrawGuidelines Canvas( modifier = Modifier .fillMaxSize() @@ -352,11 +354,11 @@ private fun CropOverlay( size = Size(width = cropRight - cropLeft, height = cropBottom - cropTop), style = Stroke(width = 1.dp.toPx()), ) - // Guide lines dividing the crop area into 9 equal parts + // Guidelines dividing the crop area into 9 equal parts if (drawGuidelines) { val thirdWidth = (cropRight - cropLeft) / 3f val thirdHeight = (cropBottom - cropTop) / 3f - (1..2).forEach { index -> + for (index in 1..2) { val offsetX = cropLeft + thirdWidth * index val offsetY = cropTop + thirdHeight * index // Vertical guide line From 4876b7c9304941f0cb38fdabd45d3234bfe90329 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 22 May 2026 10:29:31 +0000 Subject: [PATCH 227/471] Update screenshots --- ...nts.preview.imageeditor_AttachmentImageEditorView_2_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_3_en.png | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png index 553c54a63d..86b9dc57b8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d99c470fd5134e0a84b284ed32f4c93de01561e630ef32d7c22a4b476bb871b1 -size 277852 +oid sha256:7e5c43a16d24ce7efed1c90cfa9bc6d73ee12c486b2ad2823c531b87e272ac66 +size 282369 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png new file mode 100644 index 0000000000..553c54a63d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d99c470fd5134e0a84b284ed32f4c93de01561e630ef32d7c22a4b476bb871b1 +size 277852 From fe6a17e977ad9275562744c28dc6cddaef6b76d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 13:56:50 +0200 Subject: [PATCH 228/471] Remove default value of data class. --- .../impl/attachments/preview/AttachmentsPreviewPresenter.kt | 1 + .../preview/imageeditor/AttachmentImageEditModels.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 78dd6c2e82..72d840c4ad 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 @@ -276,6 +276,7 @@ class AttachmentsPreviewPresenter( imageEditorState = AttachmentImageEditorState( localMedia = originalLocalMedia, edits = appliedImageEdits, + forceDrawGuidelines = false, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt index 885bd077d1..d103e6b913 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt @@ -18,7 +18,7 @@ data class AttachmentImageEditorState( val localMedia: LocalMedia, val edits: AttachmentImageEdits, // For preview only - val forceDrawGuidelines: Boolean = false, + val forceDrawGuidelines: Boolean, ) @Immutable From 94c3bb9c4103f039a5cf4c2e652a541b113a7fe2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 13:59:21 +0200 Subject: [PATCH 229/471] Rename file. --- ...AttachmentImageEditModels.kt => AttachmentImageEditorState.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/{AttachmentImageEditModels.kt => AttachmentImageEditorState.kt} (100%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt similarity index 100% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditModels.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt From afa2a824b5c2f06206954c0c65ad6e2146a69f16 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 15:28:48 +0200 Subject: [PATCH 230/471] Fix tests. --- .../AttachmentsPreviewPresenterTest.kt | 16 ++- .../imageeditor/NormalizedCropRectTest.kt | 116 ++++++++++++------ 2 files changed, 92 insertions(+), 40 deletions(-) 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 962f800764..140d4f8ea3 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 @@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.attachments.preview.imageeditor import io.element.android.features.messages.impl.attachments.preview.imageeditor.AttachmentImageEdits import io.element.android.features.messages.impl.attachments.preview.imageeditor.EditedLocalMedia import io.element.android.features.messages.impl.attachments.preview.imageeditor.NormalizedCropRect +import io.element.android.features.messages.impl.attachments.preview.imageeditor.assertIsSimilarTo import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorState import io.element.android.features.messages.impl.attachments.video.VideoCompressionPresetSelector import io.element.android.features.messages.impl.attachments.video.VideoUploadEstimation @@ -579,7 +580,7 @@ class AttachmentsPreviewPresenterTest { editorState.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) val rotatedState = awaitItem() - assertThat(rotatedState.imageEditorState?.edits?.rotationQuarterTurns).isEqualTo(1) + assertThat(rotatedState.imageEditorState?.edits?.rotationQuarterTurns).isEqualTo(3) rotatedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) assertThat(awaitItem().isApplyingImageEdits).isTrue() @@ -630,9 +631,16 @@ class AttachmentsPreviewPresenterTest { appliedState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) val reopenedState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() - assertThat(reopenedState.imageEditorState?.localMedia?.uri).isEqualTo(originalLocalMedia.uri) - assertThat(reopenedState.imageEditorState?.edits?.cropRect).isEqualTo(cropRect) - assertThat(reopenedState.imageEditorState?.edits?.rotationDegrees).isEqualTo(90) + assertThat(reopenedState.imageEditorState!!.localMedia.uri).isEqualTo(originalLocalMedia.uri) + val rotatedCropRect = NormalizedCropRect( + left = cropRect.top, + top = 1f - cropRect.right, + right = cropRect.bottom, + bottom = 1f - cropRect.left, + ) + reopenedState.imageEditorState.edits.cropRect.assertIsSimilarTo(rotatedCropRect) + assertThat(reopenedState.imageEditorState.edits.rotationQuarterTurns).isEqualTo(3) + assertThat(reopenedState.imageEditorState.edits.rotationDegrees).isEqualTo(270) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt index b51f10727c..14cb07eed7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt @@ -25,10 +25,14 @@ class NormalizedCropRectTest { deltaX = 0.3f, deltaY = 0.1f, ) - assertThat(result.left).isEqualTo(rect.left) - assertThat(result.right).isEqualTo(rect.right) - assertThat(result.bottom).isEqualTo(rect.bottom) - assertThat(result.top).isWithin(0.0001f).of(0.3f) + result.assertIsSimilarTo( + NormalizedCropRect( + left = rect.left, + top = 0.3f, + right = rect.right, + bottom = rect.bottom, + ) + ) } @Test @@ -38,10 +42,14 @@ class NormalizedCropRectTest { deltaX = 0.1f, deltaY = 0.3f, ) - assertThat(result.top).isEqualTo(rect.top) - assertThat(result.right).isEqualTo(rect.right) - assertThat(result.bottom).isEqualTo(rect.bottom) - assertThat(result.left).isWithin(0.0001f).of(0.2f) + result.assertIsSimilarTo( + NormalizedCropRect( + left = 0.2f, + top = rect.top, + right = rect.right, + bottom = rect.bottom, + ) + ) } @Test @@ -51,10 +59,15 @@ class NormalizedCropRectTest { deltaX = -0.1f, deltaY = 0.3f, ) - assertThat(result.top).isEqualTo(rect.top) - assertThat(result.left).isEqualTo(rect.left) - assertThat(result.bottom).isEqualTo(rect.bottom) - assertThat(result.right).isWithin(0.0001f).of(0.6f) + val s = assertThat(result) + result.assertIsSimilarTo( + NormalizedCropRect( + left = rect.left, + top = rect.top, + right = 0.6f, + bottom = rect.bottom, + ) + ) } @Test @@ -64,10 +77,14 @@ class NormalizedCropRectTest { deltaX = -0.1f, deltaY = -0.3f, ) - assertThat(result.top).isEqualTo(rect.top) - assertThat(result.left).isEqualTo(rect.left) - assertThat(result.right).isEqualTo(rect.right) - assertThat(result.bottom).isWithin(0.0001f).of(0.5f) + result.assertIsSimilarTo( + NormalizedCropRect( + left = rect.left, + top = rect.top, + right = rect.right, + bottom = 0.5f, + ) + ) } @Test @@ -77,10 +94,14 @@ class NormalizedCropRectTest { deltaX = 0.1f, deltaY = 0.1f, ) - assertThat(result.top).isWithin(0.0001f).of(0.3f) - assertThat(result.left).isWithin(0.0001f).of(0.2f) - assertThat(result.right).isEqualTo(rect.right) - assertThat(result.bottom).isEqualTo(rect.bottom) + result.assertIsSimilarTo( + NormalizedCropRect( + left = 0.2f, + top = 0.3f, + right = rect.right, + bottom = rect.bottom, + ) + ) } @Test @@ -90,10 +111,14 @@ class NormalizedCropRectTest { deltaX = -0.1f, deltaY = 0.1f, ) - assertThat(result.top).isWithin(0.0001f).of(0.3f) - assertThat(result.right).isWithin(0.0001f).of(0.6f) - assertThat(result.left).isEqualTo(rect.left) - assertThat(result.bottom).isEqualTo(rect.bottom) + result.assertIsSimilarTo( + NormalizedCropRect( + left = rect.left, + top = 0.3f, + right = 0.6f, + bottom = rect.bottom, + ) + ) } @Test @@ -103,10 +128,14 @@ class NormalizedCropRectTest { deltaX = 0.1f, deltaY = -0.1f, ) - assertThat(result.bottom).isWithin(0.0001f).of(0.7f) - assertThat(result.left).isWithin(0.0001f).of(0.2f) - assertThat(result.top).isEqualTo(rect.top) - assertThat(result.right).isEqualTo(rect.right) + result.assertIsSimilarTo( + NormalizedCropRect( + left = 0.2f, + top = rect.top, + right = rect.right, + bottom = 0.7f, + ) + ) } @Test @@ -116,10 +145,14 @@ class NormalizedCropRectTest { deltaX = -0.1f, deltaY = -0.1f, ) - assertThat(result.bottom).isWithin(0.0001f).of(0.7f) - assertThat(result.right).isWithin(0.0001f).of(0.6f) - assertThat(result.top).isEqualTo(rect.top) - assertThat(result.left).isEqualTo(rect.left) + result.assertIsSimilarTo( + NormalizedCropRect( + left = rect.left, + top = rect.top, + right = 0.6f, + bottom = 0.7f, + ) + ) } @Test @@ -129,9 +162,20 @@ class NormalizedCropRectTest { deltaX = 0.6f, deltaY = 0.6f, ) - assertThat(result.left).isWithin(0.0001f).of(0.4f) - assertThat(result.top).isWithin(0.0001f).of(0.4f) - assertThat(result.right).isWithin(0.0001f).of(1.0f) - assertThat(result.bottom).isWithin(0.0001f).of(1.0f) + result.assertIsSimilarTo( + NormalizedCropRect( + left = 0.4f, + top = 0.4f, + right = 1.0f, + bottom = 1.0f, + ) + ) } } + +internal fun NormalizedCropRect.assertIsSimilarTo(expectedResult: NormalizedCropRect) { + assertThat(left).isWithin(0.0001f).of(expectedResult.left) + assertThat(top).isWithin(0.0001f).of(expectedResult.top) + assertThat(right).isWithin(0.0001f).of(expectedResult.right) + assertThat(bottom).isWithin(0.0001f).of(expectedResult.bottom) +} From 6e7444620919ffedee1ffb6efc115c7c621d9b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 22 May 2026 16:41:44 +0200 Subject: [PATCH 231/471] Allow detecting touch events outside the image by applying the drag detection to the parent node and offsetting the touch events --- .../imageeditor/AttachmentImageEditorView.kt | 107 +++++++++++------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt index 2aff3aaff1..f7c5ab6c06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -34,6 +35,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope @@ -41,6 +43,8 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.boundsInParent +import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource @@ -53,6 +57,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage @@ -188,13 +193,15 @@ fun AttachmentImageEditorView( } @Composable -private fun CropEditorCanvas( +private fun BoxScope.CropEditorCanvas( state: AttachmentImageEditorState, onCropRectChange: (NormalizedCropRect) -> Unit, ) { var imageSize by remember(state.localMedia.uri) { mutableStateOf(IntSize.Zero) } val rotationQuarterTurns = state.edits.normalizedRotationQuarterTurns + var imageRect by remember { mutableStateOf(Rect.Zero) } + BoxWithConstraints( modifier = Modifier .fillMaxSize() @@ -233,7 +240,10 @@ private fun CropEditorCanvas( Box( modifier = Modifier .size(displayedWidthDp, displayedHeightDp) - .align(Alignment.Center), + .align(Alignment.Center) + .onPlaced { + imageRect = it.boundsInParent() + }, contentAlignment = Alignment.Center, ) { if (LocalInspectionMode.current) { @@ -263,11 +273,50 @@ private fun CropEditorCanvas( } ) } + } + val touchRadius = 56.dp + var dragTarget by remember { mutableStateOf(null) } + val latestCropRect by rememberUpdatedState(state.edits.cropRect) + val drawGuidelines = dragTarget == CropDragTarget.Move || state.forceDrawGuidelines + Box( + modifier = Modifier + .fillMaxSize() + .pointerInput(Unit) { + detectDragGestures( + onDragStart = { offset -> + dragTarget = detectDragTarget( + touchPoint = offset, + imageOffset = imageRect.topLeft, + cropRect = latestCropRect, + canvasSize = Size(imageRect.width, imageRect.height), + handleTouchRadius = touchRadius.toPx(), + ) + }, + onDragCancel = { + dragTarget = null + }, + onDragEnd = { + dragTarget = null + }, + ) { change, dragAmount -> + val activeTarget = dragTarget ?: return@detectDragGestures + change.consume() + onCropRectChange( + latestCropRect.applyChange( + dragTarget = activeTarget, + deltaX = dragAmount.x / size.width.toFloat(), + deltaY = dragAmount.y / size.height.toFloat(), + ) + ) + } + }, + contentAlignment = Alignment.Center, + ) { CropOverlay( + imageSize = DpSize(displayedWidthDp, displayedHeightDp), cropRect = state.edits.cropRect, - forceDrawGuidelines = state.forceDrawGuidelines, - onCropRectChange = onCropRectChange, + drawGuidelines = drawGuidelines, ) } } @@ -275,46 +324,15 @@ private fun CropEditorCanvas( @Composable private fun CropOverlay( + imageSize: DpSize, cropRect: NormalizedCropRect, - forceDrawGuidelines: Boolean, - onCropRectChange: (NormalizedCropRect) -> Unit, + drawGuidelines: Boolean, ) { - var dragTarget by remember { mutableStateOf(null) } - val latestCropRect by rememberUpdatedState(cropRect) val borderColor = ElementTheme.colors.iconPrimary val guideColor = ElementTheme.colors.iconPrimary - val drawGuidelines = dragTarget == CropDragTarget.Move || forceDrawGuidelines + Canvas( - modifier = Modifier - .fillMaxSize() - .pointerInput(Unit) { - detectDragGestures( - onDragStart = { offset -> - dragTarget = detectDragTarget( - touchPoint = offset, - cropRect = latestCropRect, - canvasSize = Size(size.width.toFloat(), size.height.toFloat()), - handleTouchRadius = 32.dp.toPx(), - ) - }, - onDragCancel = { - dragTarget = null - }, - onDragEnd = { - dragTarget = null - }, - ) { change, dragAmount -> - val activeTarget = dragTarget ?: return@detectDragGestures - change.consume() - onCropRectChange( - latestCropRect.applyChange( - dragTarget = activeTarget, - deltaX = dragAmount.x / size.width.toFloat(), - deltaY = dragAmount.y / size.height.toFloat(), - ) - ) - } - } + modifier = Modifier.size(imageSize.width, imageSize.height) ) { val cropLeft = cropRect.left * size.width val cropTop = cropRect.top * size.height @@ -459,6 +477,7 @@ private fun fitSize( private fun detectDragTarget( touchPoint: Offset, + imageOffset: Offset, cropRect: NormalizedCropRect, canvasSize: Size, handleTouchRadius: Float, @@ -474,14 +493,14 @@ private fun detectDragTarget( CropDragTarget.Edge.Left to Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), ) corners.forEach { (target, corner) -> - if ((corner - touchPoint).getDistance() <= handleTouchRadius) { + if ((corner - touchPoint + imageOffset).getDistance() <= handleTouchRadius) { return target } } - val cropLeft = cropRect.left * canvasSize.width - val cropTop = cropRect.top * canvasSize.height - val cropRight = cropRect.right * canvasSize.width - val cropBottom = cropRect.bottom * canvasSize.height + val cropLeft = imageOffset.x + cropRect.left * canvasSize.width + val cropTop = imageOffset.y + cropRect.top * canvasSize.height + val cropRight = imageOffset.x + cropRect.right * canvasSize.width + val cropBottom = imageOffset.y + cropRect.bottom * canvasSize.height return if (touchPoint.x in cropLeft..cropRight && touchPoint.y in cropTop..cropBottom) { CropDragTarget.Move } else { From e3684e58606f6e27272fffded28754cc4d55c6f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 20:05:07 +0000 Subject: [PATCH 232/471] Update dependency androidx.javascriptengine:javascriptengine to v1.1.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a328218d79..eb8799d1f4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -98,7 +98,7 @@ androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constr androidx_camera_lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } androidx_camera_view = { module = "androidx.camera:camera-view", version.ref = "camera" } androidx_camera_camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" } -androidx_javascriptengine = "androidx.javascriptengine:javascriptengine:1.0.0" +androidx_javascriptengine = "androidx.javascriptengine:javascriptengine:1.1.0" androidx_workmanager_runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "work" } androidx_recyclerview = "androidx.recyclerview:recyclerview:1.4.0" From b31dad4b26441b91a5ae5e7fc78d0f14940f4915 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 25 May 2026 11:31:53 +0300 Subject: [PATCH 233/471] Do not show membership/profile events in public rooms (#6360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Filter some membership/profile/topic events in public rooms: don't display join and leave membership events in publicly joinable rooms, and hide display name and avatar url changes in non encrypted and publicly joinable rooms. * Add empty day post-processing to the timeline based on bxdxnn's code, tweaked. --------- Co-authored-by: Jorge Martín --- .../factories/TimelineItemsFactory.kt | 26 ++- .../factories/TimelineItemsFactoryTest.kt | 160 ++++++++++++++++++ .../matrix/impl/timeline/RustTimeline.kt | 17 +- .../RoomBeginningPostProcessor.kt | 18 +- .../impl/timeline/postprocessor/Fixtures.kt | 9 + .../RoomBeginningPostProcessorTest.kt | 117 ++++++++++++- 6 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 7b369fe6b7..dc8bdddc92 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -16,6 +16,7 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -96,7 +97,8 @@ class TimelineItemsFactory( } } val result = timelineItemGrouper.group(newTimelineItemStates).toImmutableList() - this._timelineItems.emit(result) + val filteredResult = filterEmptyDaySeparators(result) + this._timelineItems.emit(filteredResult) } private suspend fun buildAndCacheItem( @@ -114,3 +116,25 @@ class TimelineItemsFactory( return timelineItem } } + +// Remove day separators for days with no events after the client-side event filtering +internal fun filterEmptyDaySeparators(items: List): ImmutableList { + return buildList { + var hasEventBefore = false + for (item in items) { + when (item) { + is TimelineItem.Event, is TimelineItem.GroupedEvents -> { + hasEventBefore = true + add(item) + } + is TimelineItem.Virtual if item.model is TimelineItemDaySeparatorModel -> { + if (hasEventBefore) { + add(item) + } + hasEventBefore = false + } + else -> add(item) + } + } + }.toImmutableList() +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt new file mode 100644 index 0000000000..8df8d34f56 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.factories + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.fixtures.aMessageEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemDebugInfo +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts +import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel +import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel +import io.element.android.libraries.designsystem.components.avatar.anAvatarData +import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.core.FakeSendHandle +import kotlinx.collections.immutable.toImmutableList +import org.junit.Test + +class TimelineItemsFactoryTest { + private val anEvent = TimelineItem.Event( + id = UniqueId("event"), + eventId = AN_EVENT_ID, + senderId = A_USER_ID, + senderAvatar = anAvatarData(), + senderProfile = ProfileDetails.Ready(displayName = "User", displayNameAmbiguous = false, avatarUrl = null), + content = aMessageEvent().content, + reactionsState = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), + localSendState = LocalEventSendState.Sent(AN_EVENT_ID), + isEditable = false, + canBeRepliedTo = false, + inReplyTo = null, + threadInfo = null, + origin = null, + timelineItemDebugInfoProvider = { aTimelineItemDebugInfo() }, + messageShieldProvider = { null }, + sendHandleProvider = { FakeSendHandle() }, + forwarder = null, + forwarderProfile = null, + ) + + private fun aDaySeparator(date: String) = TimelineItem.Virtual( + id = UniqueId("day_$date"), + model = aTimelineItemDaySeparatorModel(date) + ) + + @Test + fun `filterEmptyDaySeparators keeps day separator with events after it`() { + val items = listOf( + anEvent, + aDaySeparator("Today"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(anEvent) + assertThat(result[1]).isEqualTo(aDaySeparator("Today")) + } + + @Test + fun `filterEmptyDaySeparators removes day separator with no events after it`() { + val items = listOf( + aDaySeparator("Today"), + aDaySeparator("Yesterday"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).isEmpty() + } + + @Test + fun `filterEmptyDaySeparators removes first day separator and keeps second when only second has events`() { + val items = listOf( + aDaySeparator("Today"), + anEvent, + aDaySeparator("Yesterday"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(anEvent) + assertThat(result[1]).isEqualTo(aDaySeparator("Yesterday")) + } + + @Test + fun `filterEmptyDaySeparators handles multiple day separators in a row with no events`() { + val items = listOf( + aDaySeparator("Today"), + aDaySeparator("Yesterday"), + aDaySeparator("Last week"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).isEmpty() + } + + @Test + fun `filterEmptyDaySeparators keeps all items when no day separators`() { + val items = listOf( + anEvent, + anEvent.copy(id = UniqueId("event2")), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(2) + } + + @Test + fun `filterEmptyDaySeparators handles grouped events after day separator`() { + val groupedEvents = TimelineItem.GroupedEvents( + id = UniqueId("grouped"), + events = listOf(anEvent).toImmutableList(), + aggregatedReadReceipts = emptyList().toImmutableList(), + ) + val items = listOf( + groupedEvents, + aDaySeparator("Today"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(groupedEvents) + assertThat(result[1]).isEqualTo(aDaySeparator("Today")) + } + + @Test + fun `filterEmptyDaySeparators removes day separator followed by non-event virtual item`() { + val readMarker = TimelineItem.Virtual( + id = UniqueId("readMarker"), + model = TimelineItemReadMarkerModel + ) + val items = listOf( + aDaySeparator("Today"), + readMarker, + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(1) + assertThat(result[0]).isEqualTo(readMarker) + } + + @Test + fun `filterEmptyDaySeparators keeps day separator when non-event virtual items are between separator and event`() { + val readMarker = TimelineItem.Virtual( + id = UniqueId("readMarker"), + model = TimelineItemReadMarkerModel + ) + val items = listOf( + anEvent, + readMarker, + aDaySeparator("Today"), + ) + val result = filterEmptyDaySeparators(items) + assertThat(result).hasSize(3) + assertThat(result[2]).isEqualTo(aDaySeparator("Today")) + } +} 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 5da4be408d..016204e1b1 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 @@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.hash.hash import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId 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 @@ -20,6 +21,7 @@ import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MsgType @@ -43,6 +45,7 @@ import io.element.android.libraries.matrix.impl.timeline.postprocessor.TypingNot import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper import io.element.android.libraries.matrix.impl.util.MessageEventContent import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -121,6 +124,13 @@ class RustTimeline( private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode) private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode) + private data class RoomTimelineInfo( + val roomCreators: ImmutableList, + val isDm: Boolean, + val joinRule: JoinRule?, + val isEncrypted: Boolean?, + ) + override val backwardPaginationStatus = MutableStateFlow( Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.PinnedEvents) ) @@ -220,20 +230,23 @@ class RustTimeline( _timelineItems, backwardPaginationStatus, forwardPaginationStatus, - joinedRoom.roomInfoFlow.map { it.creators to it.isDm }.distinctUntilChanged(), + joinedRoom.roomInfoFlow.map { RoomTimelineInfo(it.creators, it.isDm, it.joinRule, it.isEncrypted) }.distinctUntilChanged(), ) { timelineItems, backwardPaginationStatus, forwardPaginationStatus, - (roomCreators, isDm), + roomInfo, -> withContext(dispatcher) { + val (roomCreators, isDm, joinRule, isEncrypted) = roomInfo timelineItems .let { items -> roomBeginningPostProcessor.process( items = items, isDm = isDm, roomCreator = roomCreators.firstOrNull(), + joinRule = joinRule, + isEncrypted = isEncrypted, hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt index 397280231d..ec6a7a5380 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt @@ -9,33 +9,49 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.OtherState +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent /** * This timeline post-processor removes the room creation event and the self-join event from the timeline for DMs - * or add the RoomBeginning item. + * or add the RoomBeginning item. For rooms that aren't invite-only and aren't encrypted, it also removes join/leave and profile change events. */ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { fun process( items: List, isDm: Boolean, roomCreator: UserId?, + joinRule: JoinRule?, + isEncrypted: Boolean?, hasMoreToLoadBackwards: Boolean, ): List { return when { items.isEmpty() -> items mode == Timeline.Mode.PinnedEvents -> items + joinRule !is JoinRule.Invite && isEncrypted == false -> filterRoomMemberEvents(items) isDm -> processForDM(items, roomCreator) hasMoreToLoadBackwards -> items else -> processForRoom(items) } } + private fun filterRoomMemberEvents(items: List): List { + return items.filter { item -> + val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content + when (eventContent) { + is RoomMembershipContent -> eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT) + is ProfileChangeContent -> false + else -> true + } + } + } + private fun processForRoom(items: List): List { // No changes needed, timeline start item is already added by the SDK return items diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/Fixtures.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/Fixtures.kt index 50f8637096..cda5a07b8e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/Fixtures.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/Fixtures.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.timeline.aMessageContent +import io.element.android.libraries.matrix.test.timeline.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent @@ -36,6 +37,14 @@ internal val otherMemberJoinEvent = MatrixTimelineItem.Event( uniqueId = UniqueId("m.room.member_other"), event = anEventTimelineItem(content = aRoomMembershipContent(userId = A_USER_ID_2, change = MembershipChange.JOINED)) ) +internal val otherMemberLeaveEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.member_leave"), + event = anEventTimelineItem(content = aRoomMembershipContent(userId = A_USER_ID_2, change = MembershipChange.LEFT)) +) +internal val profileChangeEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("m.room.member_profile"), + event = anEventTimelineItem(content = aProfileChangeMessageContent(displayName = "New Name", prevDisplayName = "Old Name")) +) internal val messageEvent = MatrixTimelineItem.Event( uniqueId = UniqueId("m.room.message"), event = anEventTimelineItem(content = aMessageContent("hi")) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt index dbeba39973..b562847d94 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.A_USER_ID import org.junit.Test @@ -21,6 +22,8 @@ class RoomBeginningPostProcessorTest { items = emptyList(), isDm = true, roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEmpty() @@ -33,6 +36,8 @@ class RoomBeginningPostProcessorTest { items = listOf(messageEvent), isDm = true, roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(listOf(messageEvent)) @@ -45,6 +50,8 @@ class RoomBeginningPostProcessorTest { items = listOf(messageEvent), isDm = true, roomCreator = null, + joinRule = null, + isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(listOf(messageEvent)) @@ -62,6 +69,8 @@ class RoomBeginningPostProcessorTest { items = timelineItems, isDm = true, roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).containsExactly(timelineStartEvent) @@ -78,6 +87,8 @@ class RoomBeginningPostProcessorTest { items = timelineItems, isDm = true, roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(timelineItems) @@ -96,7 +107,14 @@ class RoomBeginningPostProcessorTest { messageEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = false) + val processedItems = processor.process( + timelineItems, + isDm = true, + roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, + hasMoreToLoadBackwards = false + ) assertThat(processedItems).isEqualTo(expected) } @@ -107,7 +125,14 @@ class RoomBeginningPostProcessorTest { roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + val processedItems = processor.process( + timelineItems, + isDm = true, + roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, + hasMoreToLoadBackwards = true + ) assertThat(processedItems).isEmpty() } @@ -117,7 +142,14 @@ class RoomBeginningPostProcessorTest { roomCreatorJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + val processedItems = processor.process( + timelineItems, + isDm = true, + roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, + hasMoreToLoadBackwards = true + ) assertThat(processedItems).isEmpty() } @@ -128,7 +160,84 @@ class RoomBeginningPostProcessorTest { otherMemberJoinEvent, ) val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process(timelineItems, isDm = true, roomCreator = A_USER_ID, hasMoreToLoadBackwards = true) + val processedItems = processor.process( + timelineItems, + isDm = true, + roomCreator = A_USER_ID, + joinRule = null, + isEncrypted = null, + hasMoreToLoadBackwards = true + ) assertThat(processedItems).isEqualTo(listOf(otherMemberJoinEvent)) } + + @Test + fun `processor removes join, leave, and profile events in unencrypted public rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val expected = listOf( + roomCreateEvent, + messageEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) + val processedItems = processor.process( + timelineItems, + isDm = false, + roomCreator = A_USER_ID, + joinRule = JoinRule.Public, + isEncrypted = false, + hasMoreToLoadBackwards = false + ) + assertThat(processedItems).isEqualTo(expected) + } + + @Test + fun `processor keeps all events in encrypted public rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) + val processedItems = processor.process( + timelineItems, + isDm = false, + roomCreator = A_USER_ID, + joinRule = JoinRule.Public, + isEncrypted = true, + hasMoreToLoadBackwards = false + ) + assertThat(processedItems).isEqualTo(timelineItems) + } + + @Test + fun `processor keeps membership events in invite-only rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) + val processedItems = processor.process( + timelineItems, + isDm = false, + roomCreator = A_USER_ID, + joinRule = JoinRule.Invite, + isEncrypted = null, + hasMoreToLoadBackwards = false + ) + assertThat(processedItems).isEqualTo(timelineItems) + } } From 3df85017afb43d24610a955889bca90eab325a87 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 25 May 2026 12:40:25 +0300 Subject: [PATCH 234/471] Fix formatting inconsistencies in latest event summaries (#6855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix message type prefixes formatting inconsistencies * Use new string for the poll summary prefix instead of the A11y text. Also add tests check for the bold spans. --------- Co-authored-by: Jorge Martín --- .../impl/DefaultRoomLatestEventFormatter.kt | 8 ++--- .../eventformatter/impl/PrefixWith.kt | 6 +++- .../DefaultRoomLatestEventFormatterTest.kt | 31 +++++++++++++++++-- .../src/main/res/values/localazy.xml | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt index d234e7b239..e8a462da16 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatter.kt @@ -93,8 +93,8 @@ class DefaultRoomLatestEventFormatter( message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is StickerContent -> { - val message = sp.getString(CommonStrings.common_sticker) + " (" + content.bestDescription + ")" - message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) + content.bestDescription.prefixWith(sp.getString(CommonStrings.common_sticker)) + .prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is UnableToDecryptContent -> { val message = sp.getString(CommonStrings.common_waiting_for_decryption_key) @@ -110,8 +110,8 @@ class DefaultRoomLatestEventFormatter( stateContentFormatter.format(content, senderDisambiguatedDisplayName, isOutgoing, RenderingMode.RoomList) } is PollContent -> { - val message = sp.getString(CommonStrings.common_poll_summary, content.question) - message.prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) + content.question.prefixWith(sp.getString(CommonStrings.common_poll_summary_prefix)) + .prefixIfNeeded(senderDisambiguatedDisplayName, isDmRoom, isOutgoing) } is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { val message = sp.getString(CommonStrings.common_unsupported_event) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/PrefixWith.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/PrefixWith.kt index 51fdccf256..1a04893e07 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/PrefixWith.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/PrefixWith.kt @@ -20,6 +20,10 @@ internal fun CharSequence.prefixWith(prefix: String): AnnotatedString { append(prefix) } append(": ") - append(this@prefixWith) + if (this@prefixWith is AnnotatedString) { + append(this@prefixWith) + } else { + append(this@prefixWith.toString()) + } } } diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt index e0613ed008..2e55fb0999 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLatestEventFormatterTest.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.eventformatter.impl import android.content.Context import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.font.FontWeight import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import io.element.android.libraries.matrix.api.core.UserId @@ -103,7 +104,14 @@ class DefaultRoomLatestEventFormatterTest { val info = ImageInfo(null, null, null, null, null, null, null) val message = createLatestEvent(false, null, aStickerContent(body, info, aMediaSource(url = "url"))) val result = formatter.format(message, false) - val expectedBody = someoneElseId.value + ": Sticker (a sticker body)" + val expectedBody = someoneElseId.value + ": Sticker: a sticker body" + // Check we have formatting + assertThat(result is AnnotatedString).isTrue() + // And there is a bold span for the 'Sticker' part + val boldSpanStyle = (result as AnnotatedString).spanStyles.lastOrNull { it.item.fontWeight == FontWeight.Bold } + assertThat(boldSpanStyle).isNotNull() + val spanStart = someoneElseId.value.length + 2 + assertThat(boldSpanStyle!!.start..boldSpanStyle.end).isEqualTo(spanStart..spanStart + 7) assertThat(result.toString()).isEqualTo(expectedBody) } @@ -909,10 +917,18 @@ class DefaultRoomLatestEventFormatterTest { val pollContent = aPollContent() val mineContentEvent = createLatestEvent(sentByYou = true, senderDisplayName = "Alice", content = pollContent) - assertThat(formatter.format(mineContentEvent, true)).isEqualTo("Poll: Do you like polls?") + assertThat(formatter.format(mineContentEvent, true).toString()).isEqualTo("Poll: Do you like polls?") val contentEvent = createLatestEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent) - assertThat(formatter.format(contentEvent, true)).isEqualTo("Poll: Do you like polls?") + assertThat(formatter.format(contentEvent, true).toString()).isEqualTo("Poll: Do you like polls?") + + val result = formatter.format(contentEvent, true) + // Check we have formatting + assertThat(result is AnnotatedString).isTrue() + // And there is a bold span for the 'Poll' part + val boldSpanStyle = (result as AnnotatedString).spanStyles.lastOrNull { it.item.fontWeight == FontWeight.Bold } + assertThat(boldSpanStyle).isNotNull() + assertThat(boldSpanStyle!!.start..boldSpanStyle.end).isEqualTo(0..4) } @Test @@ -925,6 +941,15 @@ class DefaultRoomLatestEventFormatterTest { val contentEvent = createLatestEvent(sentByYou = false, senderDisplayName = "Bob", content = pollContent) assertThat(formatter.format(contentEvent, false).toString()).isEqualTo("Bob: Poll: Do you like polls?") + + val result = formatter.format(contentEvent, false) + // Check we have formatting + assertThat(result is AnnotatedString).isTrue() + // And there is a bold span for the 'Poll' part + val boldSpanStyle = (result as AnnotatedString).spanStyles.lastOrNull { it.item.fontWeight == FontWeight.Bold } + assertThat(boldSpanStyle).isNotNull() + val spanStart = "Bob".length + 2 + assertThat(boldSpanStyle!!.start..boldSpanStyle.end).isEqualTo(spanStart..spanStart + 4) } // endregion diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index e89d45b228..e5bc31396f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -303,6 +303,7 @@ Reason: %1$s." "Please wait…" "Are you sure you want to end this poll?" "Poll: %1$s" + "Poll" "Total votes: %1$s" "Results will show after the poll has ended" From 8a4ff4c45663cf49eaef64b961785131ae989abc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 25 May 2026 21:16:12 +0200 Subject: [PATCH 235/471] Improve touch detection. --- .../preview/AttachmentsPreviewPresenter.kt | 2 +- .../imageeditor/AttachmentImageEditorState.kt | 29 ++++-- .../AttachmentImageEditorStateProvider.kt | 30 +++++- .../imageeditor/AttachmentImageEditorView.kt | 93 ++++++++++++++++--- 4 files changed, 128 insertions(+), 26 deletions(-) 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 72d840c4ad..cab00d99c1 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 @@ -276,7 +276,7 @@ class AttachmentsPreviewPresenter( imageEditorState = AttachmentImageEditorState( localMedia = originalLocalMedia, edits = appliedImageEdits, - forceDrawGuidelines = false, + previewDebug = false, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt index d103e6b913..3c8af52ce8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt @@ -7,6 +7,7 @@ package io.element.android.features.messages.impl.attachments.preview.imageeditor +import androidx.annotation.FloatRange import androidx.compose.runtime.Immutable import io.element.android.libraries.mediaviewer.api.local.LocalMedia @@ -18,7 +19,7 @@ data class AttachmentImageEditorState( val localMedia: LocalMedia, val edits: AttachmentImageEdits, // For preview only - val forceDrawGuidelines: Boolean, + val previewDebug: Boolean, ) @Immutable @@ -51,10 +52,10 @@ data class AttachmentImageEdits( @Immutable data class NormalizedCropRect( - val left: Float, - val top: Float, - val right: Float, - val bottom: Float, + @FloatRange(from = 0.0, to = 1.0) val left: Float, + @FloatRange(from = 0.0, to = 1.0) val top: Float, + @FloatRange(from = 0.0, to = 1.0) val right: Float, + @FloatRange(from = 0.0, to = 1.0) val bottom: Float, ) { init { require(left in 0f..1f) @@ -71,7 +72,11 @@ data class NormalizedCropRect( val height: Float get() = bottom - top - fun applyChange(dragTarget: CropDragTarget, deltaX: Float, deltaY: Float): NormalizedCropRect = when (dragTarget) { + fun applyChange( + dragTarget: CropDragTarget, + deltaX: Float, + deltaY: Float, + ): NormalizedCropRect = when (dragTarget) { is CropDragTarget.Move -> translate(deltaX, deltaY) is CropDragTarget.Corner -> dragWithCorner(dragTarget, deltaX, deltaY) is CropDragTarget.Edge -> dragWithEdge(dragTarget, deltaX, deltaY) @@ -88,7 +93,11 @@ data class NormalizedCropRect( ) } - private fun dragWithCorner(dragTarget: CropDragTarget.Corner, deltaX: Float, deltaY: Float) = when (dragTarget) { + private fun dragWithCorner( + dragTarget: CropDragTarget.Corner, + deltaX: Float, + deltaY: Float, + ) = when (dragTarget) { CropDragTarget.Corner.TopLeft -> copy( left = (left + deltaX).coerceIn(0f, right - MIN_CROP_SIZE), top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), @@ -107,7 +116,11 @@ data class NormalizedCropRect( ) } - private fun dragWithEdge(dragTarget: CropDragTarget.Edge, deltaX: Float, deltaY: Float) = when (dragTarget) { + private fun dragWithEdge( + dragTarget: CropDragTarget.Edge, + deltaX: Float, + deltaY: Float, + ) = when (dragTarget) { CropDragTarget.Edge.Top -> copy( top = (top + deltaY).coerceIn(0f, bottom - MIN_CROP_SIZE), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt index 09a4390202..df4bb5257f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt @@ -44,13 +44,37 @@ open class AttachmentImageEditorStateProvider : PreviewParameterProvider(null) } val latestCropRect by rememberUpdatedState(state.edits.cropRect) - val drawGuidelines = dragTarget == CropDragTarget.Move || state.forceDrawGuidelines + val drawGuidelines = dragTarget == CropDragTarget.Move || state.previewDebug Box( modifier = Modifier .fillMaxSize() @@ -290,7 +304,7 @@ private fun BoxScope.CropEditorCanvas( imageOffset = imageRect.topLeft, cropRect = latestCropRect, canvasSize = Size(imageRect.width, imageRect.height), - handleTouchRadius = touchRadius.toPx(), + handleTouchRadius = touchRadiusPx, ) }, onDragCancel = { @@ -317,6 +331,9 @@ private fun BoxScope.CropEditorCanvas( imageSize = DpSize(displayedWidthDp, displayedHeightDp), cropRect = state.edits.cropRect, drawGuidelines = drawGuidelines, + previewDebug = state.previewDebug, + touchRadiusPx = touchRadiusPx, + dragTarget = dragTarget, ) } } @@ -327,6 +344,9 @@ private fun CropOverlay( imageSize: DpSize, cropRect: NormalizedCropRect, drawGuidelines: Boolean, + previewDebug: Boolean, + touchRadiusPx: Float, + dragTarget: CropDragTarget?, ) { val borderColor = ElementTheme.colors.iconPrimary val guideColor = ElementTheme.colors.iconPrimary @@ -459,6 +479,32 @@ private fun CropOverlay( handleLength = handleLength, color = handleColor, ) + + if (previewDebug) { + // Draw disk around touchable area + listOf( + CropDragTarget.Edge.Top, + CropDragTarget.Edge.Right, + CropDragTarget.Edge.Bottom, + CropDragTarget.Edge.Left, + CropDragTarget.Corner.TopLeft, + CropDragTarget.Corner.TopRight, + CropDragTarget.Corner.BottomRight, + CropDragTarget.Corner.BottomLeft, + CropDragTarget.Move, + ).forEach { target -> + val color = when (target) { + is CropDragTarget.Move -> Color.Red + is CropDragTarget.Corner -> Color.Blue + is CropDragTarget.Edge -> Color.Green + }.copy(alpha = if (dragTarget == target) 9f else 0.5f) + drawCircle( + color = color, + radius = touchRadiusPx, + center = computeOffset(target, cropRect, Size(size.width, size.height)), + ) + } + } } } @@ -482,17 +528,20 @@ private fun detectDragTarget( canvasSize: Size, handleTouchRadius: Float, ): CropDragTarget? { - val corners = mapOf( - CropDragTarget.Corner.TopLeft to Offset(cropRect.left * canvasSize.width, cropRect.top * canvasSize.height), - CropDragTarget.Edge.Top to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.top * canvasSize.height), - CropDragTarget.Corner.TopRight to Offset(cropRect.right * canvasSize.width, cropRect.top * canvasSize.height), - CropDragTarget.Edge.Right to Offset(cropRect.right * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), - CropDragTarget.Corner.BottomRight to Offset(cropRect.right * canvasSize.width, cropRect.bottom * canvasSize.height), - CropDragTarget.Edge.Bottom to Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.bottom * canvasSize.height), - CropDragTarget.Corner.BottomLeft to Offset(cropRect.left * canvasSize.width, cropRect.bottom * canvasSize.height), - CropDragTarget.Edge.Left to Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f), + // Give priority on Move (extra detection of the center of crop area) + // to ensure that user can move a small crop, then to corners and at last to edges. + val handlesArea = mapOf( + CropDragTarget.Move to computeOffset(CropDragTarget.Move, cropRect, canvasSize), + CropDragTarget.Corner.TopLeft to computeOffset(CropDragTarget.Corner.TopLeft, cropRect, canvasSize), + CropDragTarget.Corner.TopRight to computeOffset(CropDragTarget.Corner.TopRight, cropRect, canvasSize), + CropDragTarget.Corner.BottomRight to computeOffset(CropDragTarget.Corner.BottomRight, cropRect, canvasSize), + CropDragTarget.Corner.BottomLeft to computeOffset(CropDragTarget.Corner.BottomLeft, cropRect, canvasSize), + CropDragTarget.Edge.Top to computeOffset(CropDragTarget.Edge.Top, cropRect, canvasSize), + CropDragTarget.Edge.Right to computeOffset(CropDragTarget.Edge.Right, cropRect, canvasSize), + CropDragTarget.Edge.Bottom to computeOffset(CropDragTarget.Edge.Bottom, cropRect, canvasSize), + CropDragTarget.Edge.Left to computeOffset(CropDragTarget.Edge.Left, cropRect, canvasSize), ) - corners.forEach { (target, corner) -> + handlesArea.forEach { (target, corner) -> if ((corner - touchPoint + imageOffset).getDistance() <= handleTouchRadius) { return target } @@ -508,6 +557,22 @@ private fun detectDragTarget( } } +private fun computeOffset( + target: CropDragTarget, + cropRect: NormalizedCropRect, + canvasSize: Size, +) = when (target) { + CropDragTarget.Move -> Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f) + CropDragTarget.Corner.TopLeft -> Offset(cropRect.left * canvasSize.width, cropRect.top * canvasSize.height) + CropDragTarget.Edge.Top -> Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.top * canvasSize.height) + CropDragTarget.Corner.TopRight -> Offset(cropRect.right * canvasSize.width, cropRect.top * canvasSize.height) + CropDragTarget.Edge.Right -> Offset(cropRect.right * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f) + CropDragTarget.Corner.BottomRight -> Offset(cropRect.right * canvasSize.width, cropRect.bottom * canvasSize.height) + CropDragTarget.Edge.Bottom -> Offset((cropRect.left + cropRect.right) * canvasSize.width / 2f, cropRect.bottom * canvasSize.height) + CropDragTarget.Corner.BottomLeft -> Offset(cropRect.left * canvasSize.width, cropRect.bottom * canvasSize.height) + CropDragTarget.Edge.Left -> Offset(cropRect.left * canvasSize.width, (cropRect.top + cropRect.bottom) * canvasSize.height / 2f) +} + // x and y are the coordinates of the corner private fun DrawScope.drawCornerHandle( x: Float, From 1f6824d285925951b56f8200927c437ccd89675c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 25 May 2026 19:34:34 +0000 Subject: [PATCH 236/471] Update screenshots --- ...nts.preview.imageeditor_AttachmentImageEditorView_2_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_4_en.png | 3 +++ ...nts.preview.imageeditor_AttachmentImageEditorView_5_en.png | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png index 86b9dc57b8..2ae328d7eb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e5c43a16d24ce7efed1c90cfa9bc6d73ee12c486b2ad2823c531b87e272ac66 -size 282369 +oid sha256:34b6dfe4e65612615c3dc87e5f65bd0b160d97527c4a4749b496bf8d48819d96 +size 256641 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png new file mode 100644 index 0000000000..6c3a6f4bf6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b15a04a861812e3e4dee0a6a30c6afef4ab171c5261d1e5bd5a234bb7296d97 +size 251908 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png new file mode 100644 index 0000000000..530c5e5bb6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c32b4744750b9f18612bded2e292dde151e2bfdd8a69a36c88044f5ca3a76f8e +size 311315 From f4475606650e45197650cd445d203fcbdb4821fd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 07:59:33 +0200 Subject: [PATCH 237/471] Update codecov/codecov-action action to v6.0.1 (#6864) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 87551c4360..842c8113f4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -108,7 +108,7 @@ jobs: # https://github.com/codecov/codecov-action - name: ☂️ Upload coverage reports to codecov - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} From 6cef5cec1bd581bbba2de772a7c4dcca7d906771 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 26 May 2026 09:22:43 +0200 Subject: [PATCH 238/471] Remove useless line. --- .../attachments/preview/imageeditor/NormalizedCropRectTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt index 14cb07eed7..c70c6169e1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/NormalizedCropRectTest.kt @@ -59,7 +59,6 @@ class NormalizedCropRectTest { deltaX = -0.1f, deltaY = 0.3f, ) - val s = assertThat(result) result.assertIsSimilarTo( NormalizedCropRect( left = rect.left, From 51aa8208e0e65d71393a964cbceb0b8f5d033100 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Tue, 26 May 2026 07:36:57 +0000 Subject: [PATCH 239/471] Add `/myroomnick` slash command --- .../impl/timeline/TimelinePresenter.kt | 27 +++++++++---- .../factories/TimelineItemsFactory.kt | 39 +++++++++++++++++++ .../libraries/matrix/api/room/JoinedRoom.kt | 7 ++++ .../matrix/impl/room/JoinedRustRoom.kt | 6 +++ .../matrix/test/room/FakeJoinedRoom.kt | 5 +++ .../libraries/slashcommands/impl/Command.kt | 2 +- .../slashcommands/impl/CommandExecutor.kt | 6 +-- .../slashcommands/impl/CommandExecutorTest.kt | 16 ++++++-- 8 files changed, 92 insertions(+), 16 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0b99c45e06..3af7326ff5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -51,6 +51,7 @@ import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin @@ -254,15 +255,25 @@ class TimelinePresenter( } .launchIn(this) + var previousItems: List? = null combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState -> - val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems) - val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items") - transaction?.putExtraData(AnalyticsUserData.TIMELINE_ITEM_COUNT, items.count().toString()) - timelineItemsFactory.replaceWith( - timelineItems = items, - roomMembers = membersState.roomMembers().orEmpty() - ) - transaction?.finish() + val roomMembers = membersState.roomMembers().orEmpty() + if (previousItems !== items) { + previousItems = items + val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems) + val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items") + transaction?.putExtraData(AnalyticsUserData.TIMELINE_ITEM_COUNT, items.count().toString()) + timelineItemsFactory.replaceWith( + timelineItems = items, + roomMembers = roomMembers + ) + transaction?.finish() + } else { + timelineItemsFactory.updateRoomMembers( + timelineItems = items, + roomMembers = roomMembers + ) + } items } .onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index dc8bdddc92..d1ec5b648a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -72,6 +72,45 @@ class TimelineItemsFactory( } } + /** + * Lightweight update that only refreshes member-derived data (e.g., read receipt display names) + * on cached items without rebuilding the diff cache or creating new items. + * Skips emission if no cached items have member-dependent state. + */ + suspend fun updateRoomMembers( + timelineItems: List, + roomMembers: List, + ) = withContext(dispatchers.computation) { + lock.withLock { + var hasUpdates = false + val updatedStates = ArrayList() + for (index in diffCache.indices().reversed()) { + val cacheItem = diffCache.get(index) + if (cacheItem is TimelineItem.Event && roomMembers.isNotEmpty()) { + val updatedItem = eventItemFactory.update( + timelineItem = cacheItem, + receivedMatrixTimelineItem = timelineItems[index] as MatrixTimelineItem.Event, + roomMembers = roomMembers + ) + diffCache[index] = updatedItem + hasUpdates = true + updatedStates.add(updatedItem) + } else if (cacheItem != null) { + updatedStates.add(cacheItem) + } else { + buildAndCacheItem(timelineItems, index, roomMembers)?.also { timelineItemState -> + updatedStates.add(timelineItemState) + } + } + } + if (hasUpdates) { + val result = timelineItemGrouper.group(updatedStates).toImmutableList() + val filteredResult = filterEmptyDaySeparators(result) + _timelineItems.emit(filteredResult) + } + } + } + private suspend fun buildAndEmitTimelineItemStates( timelineItems: List, roomMembers: List, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt index f3fefab9a8..6d8325427b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt @@ -212,4 +212,11 @@ interface JoinedRoom : BaseRoom { * @return Result indicating success or failure. */ suspend fun sendLiveLocation(geoUri: String): Result + + /** + * Sets the display name of the current user within this room. + * This is different from the global setDisplayName which updates + * the user's display name across all of their rooms. + */ + suspend fun setOwnMemberDisplayName(displayName: String): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index e75091eadc..5d85a6a625 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -546,6 +546,12 @@ class JoinedRustRoom( } } + override suspend fun setOwnMemberDisplayName(displayName: String): Result = withContext(roomDispatcher) { + runCatchingExceptions { + innerRoom.setOwnMemberDisplayName(displayName) + } + } + override fun close() = destroy() override fun destroy() { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt index b4425ddd4b..dd8acacb23 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeJoinedRoom.kt @@ -92,6 +92,7 @@ class FakeJoinedRoom( private val startLiveLocationShareResult: (Long) -> Result = { lambdaError() }, private val stopLiveLocationShareResult: () -> Result = { lambdaError() }, private val sendLiveLocationResult: (String) -> Result = { lambdaError() }, + private val setOwnMemberDisplayNameResult: (String) -> Result = { lambdaError() }, ) : JoinedRoom, BaseRoom by baseRoom { private val sendQueueUpdates = MutableSharedFlow(extraBufferCapacity = 10) @@ -255,6 +256,10 @@ class FakeJoinedRoom( sendLiveLocationResult(geoUri) } + override suspend fun setOwnMemberDisplayName(displayName: String): Result = simulateLongTask { + setOwnMemberDisplayNameResult(displayName) + } + private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) { progressCallbackValues.forEach { (current, total) -> progressCallback?.onProgress(current, total) diff --git a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/Command.kt b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/Command.kt index 0d9e1e8c72..5df871e361 100644 --- a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/Command.kt +++ b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/Command.kt @@ -109,7 +109,7 @@ enum class Command( parameters = "", description = R.string.slash_command_description_nick_for_room, isAllowedInThread = false, - isSupported = false, + isSupported = true, ), ROOM_AVATAR( command = "/roomavatar", diff --git a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutor.kt b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutor.kt index ad252cb224..01bac5825b 100644 --- a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutor.kt +++ b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutor.kt @@ -47,7 +47,7 @@ class CommandExecutor( is SlashCommand.ChangeAvatar -> changeAvatar() is SlashCommand.ChangeAvatarForRoom -> changeAvatarForRoom() is SlashCommand.ChangeDisplayName -> changeDisplayName(slashCommand) - is SlashCommand.ChangeDisplayNameForRoom -> changeDisplayNameForRoom() + is SlashCommand.ChangeDisplayNameForRoom -> changeDisplayNameForRoom(slashCommand) is SlashCommand.ChangeRoomAvatar -> changeRoomAvatar() is SlashCommand.ChangeRoomName -> changeRoomName(slashCommand) is SlashCommand.ChangeTopic -> changeTopic(slashCommand) @@ -171,8 +171,8 @@ class CommandExecutor( return Result.failure(Exception("Not yet implemented")) } - private fun changeDisplayNameForRoom(): Result { - return Result.failure(Exception("Not yet implemented")) + private suspend fun changeDisplayNameForRoom(slashCommand: SlashCommand.ChangeDisplayNameForRoom): Result { + return joinedRoom.setOwnMemberDisplayName(slashCommand.displayName) } private suspend fun changeDisplayName(slashCommand: SlashCommand.ChangeDisplayName): Result { diff --git a/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt b/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt index 497f45c96f..800cdecfb3 100644 --- a/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt +++ b/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt @@ -185,10 +185,18 @@ class CommandExecutorTest { } @Test - fun `change display name for room is not supported`() = runTest { - val sut = createCommandExecutor() - val res = sut.proceedAdmin(SlashCommand.ChangeDisplayNameForRoom(A_USER_NAME)) - assertThat(res.isFailure).isTrue() + fun `change display name for room delegates to joined room`() = runTest { + var capturedDisplayName: String? = null + val joinedRoom = FakeJoinedRoom( + setOwnMemberDisplayNameResult = { displayName -> + capturedDisplayName = displayName + Result.success(Unit) + } + ) + val sut = createCommandExecutor(joinedRoom = joinedRoom) + val res = sut.proceedAdmin(SlashCommand.ChangeDisplayNameForRoom("room nick")) + assertThat(res.isSuccess).isTrue() + assertThat(capturedDisplayName).isEqualTo("room nick") } @Test From 7b93bfbba95dd4d815fbefc7c128560fadf93add Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 09:40:07 +0200 Subject: [PATCH 240/471] Use `runBlocking` for the token refresh logic (#6863) * Use `runBlocking` for the token refresh logic The `RustClientSessionDelegate` callbacks always run in a separate thread, so they don't block the main thread. This ensures the token refresh is fully done (data saved/failed to) before the SDK continues sending the pending previously failed requests --- .../matrix/impl/RustClientSessionDelegate.kt | 49 +++++++++---------- .../matrix/impl/RustMatrixClientFactory.kt | 1 - .../impl/RustClientSessionDelegateTest.kt | 6 --- 3 files changed, 22 insertions(+), 34 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt index 690995ba77..c776ca8522 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.matrix.impl -import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.impl.core.SdkBackgroundTaskError import io.element.android.libraries.matrix.impl.mapper.toSessionData @@ -19,6 +19,7 @@ import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.ClientSessionDelegate import org.matrix.rustcomponents.sdk.Session @@ -41,14 +42,10 @@ class RustClientSessionDelegate( private val sessionStore: SessionStore, private val appCoroutineScope: CoroutineScope, private val analyticsService: AnalyticsService, - coroutineDispatchers: CoroutineDispatchers, ) : ClientSessionDelegate, ClientDelegate { // Used to ensure several calls to `didReceiveAuthError` don't trigger multiple logouts private val isLoggingOut = AtomicBoolean(false) - // To make sure only one coroutine affecting the token persistence can run at a time - private val updateTokensDispatcher = coroutineDispatchers.io.limitedParallelism(1) - // This Client needs to be set up as soon as possible so `didReceiveAuthError` can work properly. private var client: WeakReference = WeakReference(null) @@ -66,9 +63,10 @@ class RustClientSessionDelegate( this.client.clear() } + // This always runs on a background thread, so we *can* do blocking calls here, although we should avoid doing heavy work override fun saveSessionInKeychain(session: Session) { - appCoroutineScope.launch(updateTokensDispatcher) { - val existingData = sessionStore.getSession(session.userId) ?: return@launch + runCatchingExceptions { + val existingData = runBlocking { sessionStore.getSession(session.userId) } ?: return val (anonymizedAccessToken, anonymizedRefreshToken) = session.anonymizedTokens() Timber.tag(loggerTag.value).d( "Saving new session data with token: access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'. " + @@ -80,28 +78,27 @@ class RustClientSessionDelegate( passphrase = existingData.passphrase, sessionPaths = existingData.getSessionPaths(), ) - sessionStore.updateData(newData) + runBlocking { sessionStore.updateData(newData) } Timber.tag(loggerTag.value).d("Saved new session data with access token: '$anonymizedAccessToken'.") - }.invokeOnCompletion { - if (it != null) { - Timber.tag(loggerTag.value).e(it, "Failed to save new session data.") - } + }.onFailure { + Timber.tag(loggerTag.value).e(it, "Failed to save new session data.") } } + // This always runs on a background thread, so we *can* do blocking calls here, although we should avoid doing heavy work override fun didReceiveAuthError(isSoftLogout: Boolean) { - Timber.tag(loggerTag.value).w("didReceiveAuthError(isSoftLogout=$isSoftLogout)") - if (isLoggingOut.getAndSet(true).not()) { - Timber.tag(loggerTag.value).v("didReceiveAuthError -> do the cleanup") - // TODO handle isSoftLogout parameter. - appCoroutineScope.launch(updateTokensDispatcher) { + runCatchingExceptions { + Timber.tag(loggerTag.value).w("didReceiveAuthError(isSoftLogout=$isSoftLogout)") + if (isLoggingOut.getAndSet(true).not()) { + Timber.tag(loggerTag.value).v("didReceiveAuthError -> do the cleanup") + // TODO handle isSoftLogout parameter. val currentClient = client.get() if (currentClient == null) { Timber.tag(loggerTag.value).w("didReceiveAuthError -> no client, exiting") isLoggingOut.set(false) - return@launch + return } - val existingData = sessionStore.getSession(currentClient.sessionId.value) + val existingData = runBlocking { sessionStore.getSession(currentClient.sessionId.value) } val (anonymizedAccessToken, anonymizedRefreshToken) = existingData.anonymizedTokens() Timber.tag(loggerTag.value).d( "Removing session data with access token '$anonymizedAccessToken' " + @@ -110,19 +107,17 @@ class RustClientSessionDelegate( if (existingData != null) { // Set isTokenValid to false val newData = existingData.copy(isTokenValid = false) - sessionStore.updateData(newData) + runBlocking { sessionStore.updateData(newData) } Timber.tag(loggerTag.value).d("Invalidated session data with access token: '$anonymizedAccessToken'.") } else { Timber.tag(loggerTag.value).d("No session data found.") } - currentClient.logout(userInitiated = false, ignoreSdkError = true) - }.invokeOnCompletion { - if (it != null) { - Timber.tag(loggerTag.value).e(it, "Failed to remove session data.") - } + appCoroutineScope.launch { currentClient.logout(userInitiated = false, ignoreSdkError = true) } + } else { + Timber.tag(loggerTag.value).v("didReceiveAuthError -> already cleaning up") } - } else { - Timber.tag(loggerTag.value).v("didReceiveAuthError -> already cleaning up") + }.onFailure { + Timber.tag(loggerTag.value).e(it, "Failed to remove session data.") } } 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 9a573a59c1..6757edf16c 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 @@ -72,7 +72,6 @@ class RustMatrixClientFactory( sessionStore = sessionStore, appCoroutineScope = appCoroutineScope, analyticsService = analyticsService, - coroutineDispatchers = coroutineDispatchers ) suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegateTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegateTest.kt index 6aa3ef0e5b..0036f2f962 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegateTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegateTest.kt @@ -16,15 +16,11 @@ import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService -import io.element.android.tests.testutils.testCoroutineDispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import uniffi.matrix_sdk_common.BackgroundTaskFailureReason -@OptIn(ExperimentalCoroutinesApi::class) class RustClientSessionDelegateTest { @Test fun `saveSessionInKeychain should update the store`() = runTest { @@ -43,7 +39,6 @@ class RustClientSessionDelegateTest { refreshToken = "rt", ) ) - runCurrent() val result = sessionStore.getLatestSession() assertThat(result!!.accessToken).isEqualTo("at") assertThat(result.refreshToken).isEqualTo("rt") @@ -80,5 +75,4 @@ fun TestScope.aRustClientSessionDelegate( sessionStore = sessionStore, appCoroutineScope = this, analyticsService = analyticsService, - coroutineDispatchers = testCoroutineDispatchers(), ) From 0aaa80cbdc81cbf6124b3fb5f24991465d4c01a5 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Tue, 26 May 2026 10:05:07 +0200 Subject: [PATCH 241/471] Sync Strings from Localazy (#6856) Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com> --- app/src/main/res/xml/locales_config.xml | 1 + .../src/main/res/values-ca/translations.xml | 6 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 10 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 16 + .../src/main/res/values-ca/translations.xml | 13 + .../src/main/res/values-fi/translations.xml | 6 +- .../src/main/res/values-ru/translations.xml | 6 +- .../src/main/res/values-ca/translations.xml | 15 + .../src/main/res/values-ca/translations.xml | 49 + .../src/main/res/values-ca/translations.xml | 18 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-fi/translations.xml | 4 + .../src/main/res/values-ru/translations.xml | 4 + .../src/main/res/values-ca/translations.xml | 32 + .../src/main/res/values-ca/translations.xml | 35 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 41 + .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-ca/translations.xml | 38 + .../src/main/res/values-ca/translations.xml | 89 + .../src/main/res/values-fi/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 2 +- .../src/main/res/values-zh/translations.xml | 10 +- .../src/main/res/values-ca/translations.xml | 17 + .../src/main/res/values-ca/translations.xml | 68 + .../src/main/res/values-ja/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 1 + .../src/main/res/values-zh/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-ca/translations.xml | 19 + .../src/main/res/values-ca/translations.xml | 70 + .../src/main/res/values-fi/translations.xml | 16 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 35 + .../src/main/res/values-pl/translations.xml | 36 +- .../src/main/res/values-ru/translations.xml | 10 + .../src/main/res/values-zh/translations.xml | 34 + .../impl/src/main/res/values/localazy.xml | 34 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 17 + .../src/main/res/values-ca/translations.xml | 6 + .../src/main/res/values-ca/translations.xml | 61 + .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-be/translations.xml | 2 + .../src/main/res/values-bg/translations.xml | 2 + .../src/main/res/values-ca/translations.xml | 141 ++ .../src/main/res/values-cs/translations.xml | 2 + .../src/main/res/values-cy/translations.xml | 2 + .../src/main/res/values-da/translations.xml | 2 + .../src/main/res/values-de/translations.xml | 2 + .../src/main/res/values-el/translations.xml | 2 + .../src/main/res/values-es/translations.xml | 2 + .../src/main/res/values-et/translations.xml | 2 + .../src/main/res/values-eu/translations.xml | 2 + .../src/main/res/values-fa/translations.xml | 2 + .../src/main/res/values-fi/translations.xml | 2 + .../src/main/res/values-fr/translations.xml | 2 + .../src/main/res/values-hr/translations.xml | 2 + .../src/main/res/values-hu/translations.xml | 2 + .../src/main/res/values-in/translations.xml | 2 + .../src/main/res/values-it/translations.xml | 2 + .../src/main/res/values-ja/translations.xml | 2 + .../src/main/res/values-ka/translations.xml | 2 + .../src/main/res/values-ko/translations.xml | 2 + .../src/main/res/values-nb/translations.xml | 2 + .../src/main/res/values-nl/translations.xml | 2 + .../src/main/res/values-pl/translations.xml | 2 + .../main/res/values-pt-rBR/translations.xml | 2 + .../src/main/res/values-pt/translations.xml | 2 + .../src/main/res/values-ro/translations.xml | 2 + .../src/main/res/values-ru/translations.xml | 2 + .../src/main/res/values-sk/translations.xml | 2 + .../src/main/res/values-sv/translations.xml | 2 + .../src/main/res/values-tr/translations.xml | 2 + .../src/main/res/values-uk/translations.xml | 2 + .../src/main/res/values-ur/translations.xml | 2 + .../src/main/res/values-uz/translations.xml | 2 + .../src/main/res/values-vi/translations.xml | 2 + .../main/res/values-zh-rTW/translations.xml | 2 + .../src/main/res/values-zh/translations.xml | 2 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-ca/translations.xml | 20 + .../src/main/res/values-ca/translations.xml | 69 + .../src/main/res/values-ca/translations.xml | 34 + .../src/main/res/values-ca/translations.xml | 8 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-ca/translations.xml | 12 + .../src/main/res/values-ca/translations.xml | 19 + .../src/main/res/values-ca/translations.xml | 54 + .../src/main/res/values-ca/translations.xml | 4 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-ca/translations.xml | 73 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 21 + .../src/main/res/values-fi/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-ca/translations.xml | 7 + .../src/main/res/values-ca/translations.xml | 5 + .../src/main/res/values-ca/translations.xml | 83 + .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-ca/translations.xml | 11 + .../src/main/res/values-ca/translations.xml | 10 + .../src/main/res/values-ca/translations.xml | 31 + .../src/main/res/values-ca/translations.xml | 11 + .../src/main/res/values-ca/translations.xml | 387 +++ .../src/main/res/values-et/translations.xml | 5 + .../src/main/res/values-fi/translations.xml | 21 + .../src/main/res/values-fr/translations.xml | 14 + .../src/main/res/values-hu/translations.xml | 5 + .../src/main/res/values-ja/translations.xml | 16 +- .../src/main/res/values-pl/translations.xml | 13 + .../src/main/res/values-ru/translations.xml | 11 + .../src/main/res/values-zh/translations.xml | 25 +- .../src/main/res/values/localazy.xml | 7 + plugins/src/main/kotlin/extension/locales.kt | 1 + ...screens.qrcode_ShowQrCodeView_Day_1_de.png | 3 + ....roomdetails.impl_RoomDetailsDark_0_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_10_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_11_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_12_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_13_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_14_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_15_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_16_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_17_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_18_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_19_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_1_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_20_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_21_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_22_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_2_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_3_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_4_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_5_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_6_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_7_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_8_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_9_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_0_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_10_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_11_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_12_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_13_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_14_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_15_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_16_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_17_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_18_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_19_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_1_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_20_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_21_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_22_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_2_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_3_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_4_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_5_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_6_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_7_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_8_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_9_de.png | 4 +- screenshots/html/data.js | 2131 +++++++++-------- 174 files changed, 3234 insertions(+), 1184 deletions(-) create mode 100644 appnav/src/main/res/values-ca/translations.xml create mode 100644 features/analytics/api/src/main/res/values-ca/translations.xml create mode 100644 features/analytics/impl/src/main/res/values-ca/translations.xml create mode 100644 features/call/impl/src/main/res/values-ca/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-ca/translations.xml create mode 100644 features/deactivation/impl/src/main/res/values-ca/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-ca/translations.xml create mode 100644 features/home/impl/src/main/res/values-ca/translations.xml create mode 100644 features/invite/impl/src/main/res/values-ca/translations.xml create mode 100644 features/invitepeople/impl/src/main/res/values-ca/translations.xml create mode 100644 features/joinroom/impl/src/main/res/values-ca/translations.xml create mode 100644 features/knockrequests/impl/src/main/res/values-ca/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-ca/translations.xml create mode 100644 features/linknewdevice/impl/src/main/res/values-ca/translations.xml create mode 100644 features/lockscreen/impl/src/main/res/values-ca/translations.xml create mode 100644 features/login/impl/src/main/res/values-ca/translations.xml create mode 100644 features/logout/impl/src/main/res/values-ca/translations.xml create mode 100644 features/messages/impl/src/main/res/values-ca/translations.xml create mode 100644 features/poll/impl/src/main/res/values-ca/translations.xml create mode 100644 features/preferences/impl/src/main/res/values-ca/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-ca/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-ca/translations.xml create mode 100644 features/reportroom/impl/src/main/res/values-ca/translations.xml create mode 100644 features/rolesandpermissions/impl/src/main/res/values-ca/translations.xml create mode 100644 features/roomaliasresolver/impl/src/main/res/values-ca/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-ca/translations.xml create mode 100644 features/roomdetailsedit/impl/src/main/res/values-ca/translations.xml create mode 100644 features/roomdirectory/impl/src/main/res/values-ca/translations.xml create mode 100644 features/roommembermoderation/impl/src/main/res/values-ca/translations.xml create mode 100644 features/securebackup/impl/src/main/res/values-ca/translations.xml create mode 100644 features/securityandprivacy/impl/src/main/res/values-ca/translations.xml create mode 100644 features/signedout/impl/src/main/res/values-ca/translations.xml create mode 100644 features/space/impl/src/main/res/values-ca/translations.xml create mode 100644 features/startchat/impl/src/main/res/values-ca/translations.xml create mode 100644 features/userprofile/shared/src/main/res/values-ca/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-ca/translations.xml create mode 100644 libraries/dateformatter/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/matrixui/src/main/res/values-ca/translations.xml create mode 100644 libraries/mediaviewer/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/permissions/api/src/main/res/values-ca/translations.xml create mode 100644 libraries/permissions/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/pushproviders/firebase/src/main/res/values-ca/translations.xml create mode 100644 libraries/pushproviders/unifiedpush/src/main/res/values-ca/translations.xml create mode 100644 libraries/textcomposer/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/troubleshoot/impl/src/main/res/values-ca/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-ca/translations.xml create mode 100644 screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index c95b3a5cc0..a77f42817d 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -2,6 +2,7 @@ + diff --git a/appnav/src/main/res/values-ca/translations.xml b/appnav/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..d251b3a6b1 --- /dev/null +++ b/appnav/src/main/res/values-ca/translations.xml @@ -0,0 +1,6 @@ + + + "Tanca sessió i actualitza" + "%1$s ja no admet el protocol antic. Tanca sessió i torna a entrar per continuar utilitzant l\'aplicació." + "El servidor utilitzat ja no admet el protocol antic. Tanca sessió i torna-la a iniciar per continuar utilitzant l\'aplicació." + diff --git a/features/analytics/api/src/main/res/values-ca/translations.xml b/features/analytics/api/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..9f352d28ce --- /dev/null +++ b/features/analytics/api/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Comparteix dades d\'ús anònimes per ajudar-nos a identificar problemes." + "Pots llegir tots els nostres termes %1$s." + "aquí" + "Comparteix dades analítiques" + diff --git a/features/analytics/impl/src/main/res/values-ca/translations.xml b/features/analytics/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..5a2b633075 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,10 @@ + + + "No registrarem ni elaborarem perfils de cap dada personal" + "Comparteix dades d\'ús anònimes per ajudar-nos a identificar problemes." + "Pots llegir tots els nostres termes %1$s." + "aquí" + "Ho pots desactivar en qualsevol moment" + "No compartirem les teves dades amb tercers" + "Ajuda\'ns a millorar %1$s" + diff --git a/features/call/impl/src/main/res/values-ca/translations.xml b/features/call/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..92c518dbee --- /dev/null +++ b/features/call/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Trucada en curs" + "Toca per tornar a la trucada" + "☎️ Trucada en curs" + "Element Call entrant" + diff --git a/features/createroom/impl/src/main/res/values-ca/translations.xml b/features/createroom/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..f9e1dea0fc --- /dev/null +++ b/features/createroom/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,16 @@ + + + "Sala nova" + "Convida persones" + "S\'ha produït un error en crear la sala" + "Només s\'hi poden unir les persones convidades." + "Tothom pot trobar aquesta sala. +Pots canviar-ho en qualsevol moment a la configuració de sala." + "Qualsevol persona pot sol·licitar unir-s\'hi però un administrador o moderador l\'haurà d\'acceptar" + "Permet sol·licituds d\'unió" + "Tothom pot unir-s\'hi." + "És necessària una adreça perquè sigui visible al directori públic." + "Adreça" + "Visibilitat de sala" + "Tema (opcional)" + diff --git a/features/deactivation/impl/src/main/res/values-ca/translations.xml b/features/deactivation/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..c8ad39f7ae --- /dev/null +++ b/features/deactivation/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,13 @@ + + + "Si us plau, confirma que vols desactivar el teu compte. Aquesta acció no es pot desfer." + "Elimina tots els meus missatges" + "Avís: els futurs usuaris podrien veure converses incompletes." + "La desactivació del compte és %1$s, implica:" + "irreversible" + "%1$s el compte (no podràs tornar a iniciar sessió i el teu ID no es podrà reutilitzar)." + "Desactiva permanentment" + "Se t\'eliminarà de totes les sales o xats." + "S\'eliminarà la informació del compte del nostre servidor d\'identitat." + "Els teus missatges continuaran sent visibles per als usuaris registrats, però no estaran disponibles per a usuaris nous o no registrats si decideixes eliminar-los." + diff --git a/features/deactivation/impl/src/main/res/values-fi/translations.xml b/features/deactivation/impl/src/main/res/values-fi/translations.xml index df2543be70..148fe3d610 100644 --- a/features/deactivation/impl/src/main/res/values-fi/translations.xml +++ b/features/deactivation/impl/src/main/res/values-fi/translations.xml @@ -1,14 +1,14 @@ - "Vahvista, että haluat deaktivoida tilisi. Tätä ei voi perua." + "Vahvista, että haluat poistaa tilisi. Tätä ei voi perua." "Poista kaikki viestini" "Varoitus: Tulevaisuudessa muut voivat nähdä puutteellisia keskusteluja." - "Tilisi deaktivointia %1$s. Jos teet sen:" + "Tilisi poistamista %1$s. Jos teet sen:" "ei voi peruuttaa" "Tilisi %1$s (et voi kirjautua takaisin sisään, eikä tunnustasi voi käyttää uudelleen)." "poistetaan käytöstä pysyvästi" "Sinut poistetaan kaikista keskusteluhuoneista." "Tilitietosi poistetaan identiteettipalvelimeltamme." "Viestisi näkyvät edelleen rekisteröityneille käyttäjille, mutta ne eivät ole uusien tai rekisteröimättömien käyttäjien saatavilla, jos päätät poistaa ne." - "Deaktivoi tili" + "Poista tili" diff --git a/features/deactivation/impl/src/main/res/values-ru/translations.xml b/features/deactivation/impl/src/main/res/values-ru/translations.xml index 6f595cde29..79c25af475 100644 --- a/features/deactivation/impl/src/main/res/values-ru/translations.xml +++ b/features/deactivation/impl/src/main/res/values-ru/translations.xml @@ -1,14 +1,14 @@ - "Вы уверены, что хотите отключить свою учётную запись? Данное действие необратимо." + "Вы уверены, что хотите удалить свою учётную запись? Данное действие необратимо." "Удалить все мои сообщения" "Внимание: в будущем пользователи могут видеть неполные переписки." - "Деактивация вашего аккаунта %1$s и означает следующее:" + "Удаление вашего аккаунта %1$s, это означает следующее:" "необратимо" "Ваша учётная запись будет %1$s (вы не сможете войти в неё снова, и другие пользователи не смогут использовать ваше имя пользователя)." "Отключить навсегда" "Вы будете удалены из всех чатов." "Данные Вашего аккаунта будут удалены с нашего сервера идентификации." "Ваши сообщения по-прежнему будут видны зарегистрированным пользователям, но не будут доступны новым или незарегистрированным пользователям, если вы решите удалить их." - "Отключить учётную запись" + "Удалить аккаунт" diff --git a/features/ftue/impl/src/main/res/values-ca/translations.xml b/features/ftue/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..1b645dd5eb --- /dev/null +++ b/features/ftue/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,15 @@ + + + "No pots confirmar-la?" + "Crea nova clau de recuperació" + "Verifica aquest dispositiu per configurar missatges segurs." + "Confirma la teva identitat" + "Utilitza un altre dispositiu" + "Utilitza clau de recuperació" + "Ara pots llegir o enviar missatges de manera segura, i qualsevol persona amb qui xategis també confiarà en aquest dispositiu." + "Dispositiu verificat" + "Utilitza un altre dispositiu" + "Esperant un altre dispositiu…" + "Pots canviar la configuració més tard." + "Permet les notificacions i no perdis cap missatge" + diff --git a/features/home/impl/src/main/res/values-ca/translations.xml b/features/home/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..e37db27f23 --- /dev/null +++ b/features/home/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,49 @@ + + + "Desactiva l\'optimització de bateria d\'aquesta aplicació per assegurar-te de rebre totes les notificacions." + "No arriben les notificacions?" + "Recupera la teva identitat criptogràfica i l\'historial de missatges amb una clau de recuperació si has perdut l\'accés a tots els teus dispositius existents." + "Configura la recuperació" + "Configura la recuperació per protegir el teu compte" + "Confirma la clau de recuperació per mantenir l\'accés a l\'emmagatzematge de claus i a l\'historial de missatges." + "Introdueix clau de recuperació" + "Has oblidat la clau de recuperació?" + "L\'emmagatzematge de claus no està sincronitzat" + "Per assegurar que mai et perdis una trucada important, canvia la configuració per permetre les notificacions en pantalla completa quan el telèfon està bloquejat." + "Millora l\'experiència de les trucades" + "Xats" + "Segur que vols rebutjar la invitació per unir-te a %1$s?" + "Rebutja invitació" + "Segur que vols rebutjar el xat privat amb %1$s?" + "Rebutja xat" + "Sense invitacions" + "%1$s (%2$s) t\'ha convidat" + "Aquest procés només s\'ha de fer una vegada, gràcies per esperar." + "Configurant compte." + "Crea un nou xat o sala" + "Comença enviant un missatge a algú." + "Encara no hi ha xats." + "Preferits" + "Pots afegir un xat a preferits a la configuració del xat. +De moment, pots desseleccionar els filtres per veure tots els xats." + "Encara no tens cap xat preferit" + "Invitacions" + "No tens cap invitació pendent." + "Prioritat baixa" + "Pots desseleccionar els filtres per veure els altres xats" + "Cap xats per a aquesta selecció" + "Persones" + "Encara no tens cap xat directe" + "Sales" + "Encara no pertanys a cap sala" + "No llegits" + "Enhorabona! +No tens missatges sense llegir!" + "Sol·licitud d\'unió enviada" + "Xats" + "Marca com a llegit" + "Marca com a no llegit" + "La sala ha estat actualitzada" + "Sembla que estàs utilitzant un dispositiu nou. Verifica\'l amb un altre dispositiu per accedir als teus missatges xifrats." + "Verifica que ets tu" + diff --git a/features/invite/impl/src/main/res/values-ca/translations.xml b/features/invite/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..0dd3e42664 --- /dev/null +++ b/features/invite/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,18 @@ + + + "No veuràs cap missatge ni invitacions a sales d\'aquest usuari." + "Bloqueja usuari" + "Denuncia aquesta sala al teu proveïdor de compte." + "Descriu el motiu de la denúncia…" + "Rebutja i bloqueja" + "Segur que vols rebutjar la invitació per unir-te a %1$s?" + "Rebutja invitació" + "Segur que vols rebutjar el xat privat amb %1$s?" + "Rebutja xat" + "Sense invitacions" + "%1$s (%2$s) t\'ha convidat" + "Sí, rebutja i bloqueja" + "Segur que vols rebutjar la invitació d\'unió a aquesta sala? Això també evitarà que %1$s et contacti i et convidi a sales." + "Rebutja la invitació i bloqueja" + "Rebutja i bloqueja" + diff --git a/features/invitepeople/impl/src/main/res/values-ca/translations.xml b/features/invitepeople/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..294d04b4a3 --- /dev/null +++ b/features/invitepeople/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "Ja és membre" + "Ja s\'ha convidat" + diff --git a/features/invitepeople/impl/src/main/res/values-fi/translations.xml b/features/invitepeople/impl/src/main/res/values-fi/translations.xml index e347919719..d2283c7b2a 100644 --- a/features/invitepeople/impl/src/main/res/values-fi/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-fi/translations.xml @@ -2,4 +2,8 @@ "On jo jäsen" "On jo kutsuttu" + "Sinulla ei ole tällä hetkellä keskusteluja näiden yhteystietojen kanssa. Vahvista kutsusi heille tähän huoneeseen ennen kuin jatkat." + "Sinulla ei ole tällä hetkellä keskusteluja tämän yhteystiedon kanssa. Vahvista kutsusi hänelle tähän huoneeseen ennen kuin jatkat." + "Kutsutaanko uusia yhteystietoja tähän huoneeseen?" + "Kutsutaanko uusi yhteystieto tähän huoneeseen?" diff --git a/features/invitepeople/impl/src/main/res/values-ru/translations.xml b/features/invitepeople/impl/src/main/res/values-ru/translations.xml index 45e650f081..1f2455ba72 100644 --- a/features/invitepeople/impl/src/main/res/values-ru/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-ru/translations.xml @@ -2,4 +2,8 @@ "Уже участник" "Уже приглашен(а)" + "У тебя пока нет чатов с этими контактами. Подтверди приглашение в эту комнату, прежде чем продолжить." + "У вас пока нет чатов с этим контактом. Подтверди приглашение в эту комнату, прежде чем продолжить." + "Пригласить новых участников в эту комнату?" + "Пригласить нового участника в эту комнату?" diff --git a/features/joinroom/impl/src/main/res/values-ca/translations.xml b/features/joinroom/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..e26c33d40f --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,32 @@ + + + "Has estat bandejat per %1$s." + "T\'han bandejat" + "Motiu: %1$s." + "Cancel·la la sol·licitud" + "Sí, cancel·la" + "Segur que vols cancel·lar la teva sol·licitud d\'unió a aquesta sala?" + "Cancel·la la sol·licitud d\'unió" + "Sí, rebutja i bloqueja" + "Segur que vols rebutjar la invitació d\'unió a aquesta sala? Això també evitarà que %1$s et contacti i et convidi a sales." + "Rebutja la invitació i bloqueja" + "Rebutja i bloqueja" + "Ha fallat la unió" + "O t\'han de convidar per unir-te o hi pot haver restriccions d\'accés." + "Oblida" + "Per unir-te, necessites una invitació" + "Uneix-te" + "Pot ser que t\'hagin de convidar o hagis de ser membre d\'un espai per unir-t\'hi." + "Envia sol·licitud d\'unió" + "Missatge (opcional)" + "Rebràs una invitació per unir-te a la sala si la teva sol·licitud és acceptada." + "Sol·licitud d\'unió enviada" + "No s\'ha pogut mostrar la vista prèvia de la sala. Pot ser degut a problemes de xarxa o del servidor." + "No s\'ha pogut mostrar la vista prèvia de la sala" + "%1$s encara no admet els espais. Pots accedir-hi a través del navegador web." + "Espais encara no compatibles" + "Clic al botó següent i s\'avisarà a un administrador de sala. Podràs unir-te un cop t\'hagi aprovat." + "Per poder veure l\'historial de missatges has de ser un membre de la sala." + "Vols unir-te a aquesta sala?" + "Vista prèvia no disponible" + diff --git a/features/knockrequests/impl/src/main/res/values-ca/translations.xml b/features/knockrequests/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..bdf1cac21b --- /dev/null +++ b/features/knockrequests/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,35 @@ + + + "Sí, accepta-les totes" + "Segur que vols acceptar totes les sol·licituds d\'unió?" + "Accepta totes les sol·licituds" + "Accepta-les totes" + "No s\'han pogut acceptar totes les sol·licituds. Vols tornar-ho a intentar?" + "No s\'han pogut acceptar totes les sol·licituds" + "Acceptant totes les sol·licituds d\'unió" + "No s\'ha pogut acceptar la sol·licitud. Vols tornar-ho a intentar?" + "No s\'ha pogut acceptar la sol·licitud" + "Acceptant sol·licitud d\'unió" + "Sí, rebutja i bandeja" + "Segur que vols rebutjar i bandejar %1$s? L\'usuari no podrà sol·licitar de nou l\'accés d\'unió a aquesta sala." + "Rebutja i bandeja l\'accés" + "Rebutjant i bandejant l\'accés" + "Sí, rebutja" + "Segur que vols rebutjar %1$s d\'unir-se a aquesta sala?" + "Rebutja l\'accés" + "Rebutja i bandeja" + "No s\'ha pogut rebutjar la sol·licitud. Vols tornar-ho a intentar?" + "No s\'ha pogut rebutjar la sol·licitud" + "Rebutjant sol·licitud d\'unió" + "Quan algú demani unir-se a la sala, aquí podràs veure la sol·licitud." + "No hi ha sol·licituds d\'unió pendents" + "Carregant sol·licituds d\'unió…" + "Sol·licituds d\'unió" + + "%1$s +%2$d altre volen unir-se a la sala" + "%1$s +%2$d altres volen unir-se a la sala" + + "Veure totes" + "Accepta" + "%1$s vol unir-se a aquesta sala" + diff --git a/features/knockrequests/impl/src/main/res/values-zh/translations.xml b/features/knockrequests/impl/src/main/res/values-zh/translations.xml index d70632861c..d8b8f76b13 100644 --- a/features/knockrequests/impl/src/main/res/values-zh/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-zh/translations.xml @@ -17,7 +17,7 @@ "是,拒绝" "你确定要拒绝 %1$s 加入此房间的申请?" "拒绝访问" - "拒绝和禁止" + "拒绝并封禁" "我们无法拒绝此申请。是否重试?" "拒绝申请失败" "拒绝加入申请" diff --git a/features/leaveroom/api/src/main/res/values-ca/translations.xml b/features/leaveroom/api/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..870e0c35f2 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Segur que vols sortir d\'aquest xat? El xat no és públic i no t\'hi podràs tornar a unir sense una invitació." + "Segur que vols sortir d\'aquesta sala? N\'ets l\'única persona. Si en surts, ningú s\'hi podrà unir i tu tampoc." + "Segur que vols sortir d\'aquesta sala? La sala no és pública i no podràs tornar a unir-t\'hi sense una invitació." + "Segur que vols sortir de la sala?" + diff --git a/features/linknewdevice/impl/src/main/res/values-ca/translations.xml b/features/linknewdevice/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..d78744ff85 --- /dev/null +++ b/features/linknewdevice/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,41 @@ + + + "Escaneja el QR" + "Escaneja el codi QR amb aquest dispositiu" + "Preparat per escanejar" + "El proveïdor del teu compte no admet %1$s." + "%1$s no és compatible" + "Codi QR no compatible" + "L\'inici de sessió s\'ha cancel·lat a l\'altre dispositiu." + "Sol·licitud d\'inici de sessió cancel·lada" + "Inici de sessió ha caducat. Torna-ho a provar." + "L\'inici de sessió no s\'ha completat a temps" + "Selecciona %1$s" + "No s\'ha pogut establir una connexió segura amb el dispositiu nou. Els dispositius existents continuen sent segurs, no te n\'has de preocupar." + "I ara què?" + "Prova de tornar a iniciar sessió mitjançant un codi QR si es tracta d\'un problema de xarxa." + "Si es repeteix el mateix problema, prova una xarxa wifi diferent o utilitza les dades mòbils en lloc del wifi." + "Si no funciona, inicia sessió manualment" + "Connexió no segura" + "Se\'t demanarà que introdueixis els dos dígits mostrats en aquest dispositiu." + "Introdueix el número següent a l\'altre dispositiu" + "L\'inici de sessió s\'ha cancel·lat a l\'altre dispositiu." + "Sol·licitud d\'inici de sessió cancel·lada" + "L\'inici de sessió s\'ha rebutjat a l\'altre dispositiu." + "Inici de sessió rebutjat" + "Inici de sessió ha caducat. Torna-ho a provar." + "L\'inici de sessió no s\'ha completat a temps" + "El teu altre dispositiu no admet l\'inici de sessió a %s amb codis QR. + +Prova d\'iniciar la sessió manualment o escaneja el QR amb un altre dispositiu." + "Codi QR no compatible" + "El proveïdor del teu compte no admet %1$s." + "%1$s no és compatible" + "Utilitza el codi QR que es mostra a l\'altre dispositiu." + "Torna-ho a intentar" + "Codi QR incorrecte" + "Per continuar, has donar permís a %1$s per poder utilitzar la càmera del dispositiu." + "Permet l\'accés a la càmera per poder escanejar el codi QR" + "S\'ha produït un error inesperat. Torna-ho a provar." + "Esperant el teu altre dispositiu" + diff --git a/features/linknewdevice/impl/src/main/res/values-zh/translations.xml b/features/linknewdevice/impl/src/main/res/values-zh/translations.xml index cabcb5ccd0..f11f60dd61 100644 --- a/features/linknewdevice/impl/src/main/res/values-zh/translations.xml +++ b/features/linknewdevice/impl/src/main/res/values-zh/translations.xml @@ -9,7 +9,7 @@ "输入两位数字的代码" "这将验证你与其它设备的连接是否安全。" "请输入另一台设备上显示的数字" - "账户提供方不支持 %1$s." + "账户提供者不支持 %1$s." "不支持 %1$s." "你的账户提供者不支持使用二维码登录到新设备。" "二维码不受支持" @@ -48,7 +48,7 @@ 尝试手动或使用另一个设备扫描二维码." "二维码不受支持" - "账户提供方不支持 %1$s." + "账户提供者不支持 %1$s." "不支持 %1$s." "使用其它设备上显示的二维码。" "重试" diff --git a/features/location/impl/src/main/res/values-fi/translations.xml b/features/location/impl/src/main/res/values-fi/translations.xml index 4a7d801f71..b35d11cd49 100644 --- a/features/location/impl/src/main/res/values-fi/translations.xml +++ b/features/location/impl/src/main/res/values-fi/translations.xml @@ -2,4 +2,5 @@ "Reaaliaikainen sijaintihistoriasi tallennetaan huoneeseen ja on jäsenten nähtävissä istunnon päätyttyä." "Valitse, kuinka kauan haluat jakaa reaaliaikaisen sijaintisi." + "Sinulla ei ole oikeuksia jakaa reaaliaikaista sijaintiasi tässä huoneessa" diff --git a/features/location/impl/src/main/res/values-ja/translations.xml b/features/location/impl/src/main/res/values-ja/translations.xml index 971693ef11..1060120ee5 100644 --- a/features/location/impl/src/main/res/values-ja/translations.xml +++ b/features/location/impl/src/main/res/values-ja/translations.xml @@ -2,4 +2,5 @@ "ライブ位置情報の履歴はルームに保管され、メンバーは後から確認することもできます。" "ライブ位置情報を共有する期間を選択してください。" + "このルームでライブ位置情報を共有する権限がありません。" diff --git a/features/location/impl/src/main/res/values-ru/translations.xml b/features/location/impl/src/main/res/values-ru/translations.xml index 3c496f1d39..97b279621c 100644 --- a/features/location/impl/src/main/res/values-ru/translations.xml +++ b/features/location/impl/src/main/res/values-ru/translations.xml @@ -2,4 +2,5 @@ "История вашего местоположения в режиме реального времени будет сохранена в комнате и станет доступна участникам после окончания сессии." "Выберите, как долго вы будете делиться своим местоположением в режиме реального времени." + "У тебя нет прав на то, чтобы делиться своим текущим местоположением в этой комнате" diff --git a/features/lockscreen/impl/src/main/res/values-ca/translations.xml b/features/lockscreen/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..5637977b97 --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,38 @@ + + + "l\'autenticació biomètrica" + "desbloqueig biomètric" + "Desbloqueja amb biometria" + "Confirma biometria" + "Has oblidat el PIN?" + "Canvia codi PIN" + "Permet desbloqueig biomètric" + "Elimina PIN" + "Segur que vols eliminar el PIN?" + "Vols eliminar el PIN?" + "Permet %1$s" + "Prefereixo utilitzar el PIN" + "Estalvia\'t temps i utilitza %1$s per desbloquejar l\'aplicació" + "Escull el PIN" + "Confirma PIN" + "Bloqueja %1$s per afegir més seguretat als teus xats. + +Escull alguna cosa que recordis. Si oblides aquest PIN, es tancarà sessió a l\'aplicació." + "Per motius de seguretat no pots utilitzar aquest codi PIN" + "Escull un PIN diferent" + "Introdueix el mateix PIN dues vegades" + "Els codis PIN no coincideixen" + "Hauràs de tornar a iniciar sessió i crear un nou PIN per continuar." + "S\'està tancant la sessió" + + "Tens %1$d intent per desbloquejar" + "Tens %1$d intents per desbloquejar" + + + "PIN incorrecte. Tens %1$d intent més" + "PIN incorrecte. Tens %1$d intents més" + + "Utilitza biometria" + "Utilitza PIN" + "S\'està tancant la sessió…" + diff --git a/features/login/impl/src/main/res/values-ca/translations.xml b/features/login/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..f079e7b071 --- /dev/null +++ b/features/login/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,89 @@ + + + "Canvia el proveïdor del compte" + "Adreça del servidor" + "Introdueix una paraula de cerca o un domini (adreça)." + "Cerca una empresa, una comunitat o un servidor privat." + "Busca un proveïdor de comptes" + "Aquí és on es guardaran els teus xats, de manera similar a utilitzar un proveïdor de correu electrònic per guardar els teus correus electrònics." + "Estàs a punt d\'iniciar sessió a %s" + "Aquí és on es guardaran els teus xats, de manera similar a utilitzar un proveïdor de correu electrònic per guardar els teus correus electrònics." + "Estàs a punt de crear un compte a %s" + "Matrix.org és un gran servidor gratuït de la xarxa pública de Matrix per a la comunicació segura i descentralitzada, gestionat per la Fundació Matrix.org." + "Altres" + "Utilitza un proveïdor de comptes diferent, com ara el teu servidor privat o un compte de feina." + "Canvia el proveïdor del compte" + "No s\'ha pogut accedir a aquest servidor. Comprova que hagis introduït correctament l\'URL del servidor. Si ja és correcte, posa\'t en contacte amb l\'administrador del servidor per a més informació." + "Servidor no disponible a causa d\'un problema al fitxer .well-known: +%1$s" + "URL del servidor" + "Introdueix un domini." + "Quina és l\'adreça del teu servidor?" + "Selecciona el teu servidor" + "Crea un compte" + "Aquest compte s\'ha desactivat." + "Usuari i/o contrasenya incorrectes" + "Identificador d\'usuari invàlid. Format esperat: ‘@usuari:servidor.org’" + "Aquest servidor està configurat per utilitzar tokens d\'actualització, però no són compatibles quan s\'utilitza l\'inici de sessió basat en contrasenya." + "El servidor seleccionat no admet contrasenya o inici de sessió OAuth. Posa\'t en contacte amb l\'administrador o tria un altre servidor." + "Introdueix les teves dades" + "Matrix és una xarxa oberta per a comunicacions segures i descentralitzades." + "Hola de nou!" + "Inicia sessió a %1$s" + "Inicia sessió manualment" + "Inicia sessió a %1$s" + "Inicia sessió amb un codi QR" + "Crea un compte" + "Et donem la benvinguda a %1$s. Més ràpid i simple que mai." + "Et donem la benvinguda a %1$s. Dissenyat per ser més ràpid i simple." + "Sigues el teu propi element" + "Establint una connexió segura" + "No s\'ha pogut establir una connexió segura amb el dispositiu nou. Els dispositius existents continuen sent segurs, no te n\'has de preocupar." + "I ara què?" + "Prova de tornar a iniciar sessió mitjançant un codi QR si es tracta d\'un problema de xarxa." + "Si es repeteix el mateix problema, prova una xarxa wifi diferent o utilitza les dades mòbils en lloc del wifi." + "Si no funciona, inicia sessió manualment" + "Connexió no segura" + "Se\'t demanarà que introdueixis els dos dígits mostrats en aquest dispositiu." + "Introdueix el número següent a l\'altre dispositiu" + "Inicia sessió a l\'altre dispositiu i torna-ho a provar o utilitza un altre dispositiu amb la sessió ja iniciada." + "No s\'ha iniciat sessió a l\'altre dispositiu" + "L\'inici de sessió s\'ha cancel·lat a l\'altre dispositiu." + "Sol·licitud d\'inici de sessió cancel·lada" + "L\'inici de sessió s\'ha rebutjat a l\'altre dispositiu." + "Inici de sessió rebutjat" + "Inici de sessió ha caducat. Torna-ho a provar." + "L\'inici de sessió no s\'ha completat a temps" + "El teu altre dispositiu no admet l\'inici de sessió a %s amb codis QR. + +Prova d\'iniciar la sessió manualment o escaneja el QR amb un altre dispositiu." + "Codi QR no compatible" + "El proveïdor del teu compte no admet %1$s." + "%1$s no és compatible" + "Preparat per escanejar" + "Obre %1$s en un dispositiu d\'escriptori" + "Clica la teva imatge" + "Selecciona %1$s" + "“Enllaça nou dispositiu”" + "Escaneja el codi QR amb aquest dispositiu" + "Només disponible si el proveïdor del compte ho admet." + "Obre %1$s en un altre dispositiu per obtenir el codi QR" + "Utilitza el codi QR que es mostra a l\'altre dispositiu." + "Torna-ho a intentar" + "Codi QR incorrecte" + "Vés a la configuració de càmera" + "Per continuar, has donar permís a %1$s per poder utilitzar la càmera del dispositiu." + "Permet l\'accés a la càmera per poder escanejar el codi QR" + "Escaneja el QR" + "Torna a començar" + "S\'ha produït un error inesperat. Torna-ho a provar." + "Esperant el teu altre dispositiu" + "Per verificar l\'inici de sessió, pot ser que el proveïdor del teu compte et demani el següent codi." + "Codi de verificació" + "Canvia el proveïdor del compte" + "Servidor privat per a treballadors d\'Element." + "Matrix és una xarxa oberta per a comunicacions segures i descentralitzades." + "Aquí és on es guardaran els teus xats, de manera similar a utilitzar un proveïdor de correu electrònic per guardar els teus correus electrònics." + "Estàs a punt d\'iniciar sessió a %1$s" + "Estàs a punt de crear un compte a %1$s" + diff --git a/features/login/impl/src/main/res/values-fi/translations.xml b/features/login/impl/src/main/res/values-fi/translations.xml index 4f5225be67..8561bcfce3 100644 --- a/features/login/impl/src/main/res/values-fi/translations.xml +++ b/features/login/impl/src/main/res/values-fi/translations.xml @@ -28,7 +28,7 @@ "Mikä on palvelimesi osoite?" "Valitse palvelimesi" "Luo tili" - "Tämä tili on deaktivoitu." + "Tämä tili on poistettu." "Väärä käyttäjänimi ja/tai salasana" "Tämä ei ole kelvollinen käyttäjätunnus. Odotettu muoto: \'@käyttäjä:kotipalvelin.fi\'" "Tämä palvelin on määritetty käyttämään refresh tokeneja. Näitä ei tueta salasanapohjaisen kirjautumisen kanssa." diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index 329bad30af..564fc87cac 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -28,7 +28,7 @@ "Какой адрес у вашего сервера?" "Выберите свой сервер" "Создать аккаунт" - "Данная учётная запись была отключена." + "Эта учетная запись была удалена." "Неверное имя пользователя и/или пароль" "Это некорректный идентификатор пользователя. Правильный формат: @user:homeserver.org" "Этот сервер настроен на использование токенов обновления. Они не поддерживаются при использовании входа на основе пароля." diff --git a/features/login/impl/src/main/res/values-zh/translations.xml b/features/login/impl/src/main/res/values-zh/translations.xml index 8e86b51d7b..16afe7d5bd 100644 --- a/features/login/impl/src/main/res/values-zh/translations.xml +++ b/features/login/impl/src/main/res/values-zh/translations.xml @@ -1,10 +1,10 @@ - "更改账户提供方" + "更改账户提供者" "主服务器地址" "输入搜索词或域名地址。" "搜索公司、社区或私人服务器。" - "寻找账户提供方" + "查找账户提供者" "这是你的对话将存在的地方,就像你使用邮件提供者来存储电子邮件那样。" "你即将登录到 %s" "这是你的对话将存在的地方,就像你使用邮件提供者来存储电子邮件那样。" @@ -12,7 +12,7 @@ "Matrix.org 由 Matrix.org 基金会运营,是用于安全、去中心化的通信的公共 Matrix 网络上的大型免费服务器。" "其它" "使用其它账户提供者,例如你自己的私有服务器或工作账户。" - "更改账户提供方" + "更改账户提供者" "Google Play" "%1$s 要求 Element Pro。请从应用商店下载。" "需要 Element Pro" @@ -77,7 +77,7 @@ 尝试手动或使用另一个设备扫描二维码." "二维码不受支持" - "账户提供方不支持 %1$s." + "账户提供者不支持 %1$s." "不支持 %1$s." "准备进行扫描" "在桌面设备上打开 %1$s" @@ -99,7 +99,7 @@ "正在等待其它设备" "你的账户提供者可能会要求你提供以下代码以验证登录。" "你的验证码" - "更改账户提供方" + "更改账户提供者" "专为 Element 员工提供的私人服务器。" "Matrix 是一个用于安全、去中心化通信的开放网络。" "这是你的对话将存在的地方,就像你使用邮件提供者来存储电子邮件那样。" diff --git a/features/logout/impl/src/main/res/values-ca/translations.xml b/features/logout/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..eb0c82bb15 --- /dev/null +++ b/features/logout/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,17 @@ + + + "Segur que vols tancar sessió?" + "Tanca sessió" + "Tanca sessió" + "S\'està tancant la sessió…" + "Estàs a punt de tancar sessió. Si tanques sessió ara, perdràs l\'accés als missatges xifrats." + "Has desactivat la còpia de seguretat" + "Encara tens una còpia de seguretat de les teves claus i t\'has desconnectat. Torna a connectar-te per poder fer una còpia de seguretat de les teves claus abans de tancar la sessió." + "Encara s\'està fent una còpia de seguretat de les teves claus" + "Espera a que s\'hagi completat abans de tancar sessió." + "Encara s\'està fent una còpia de seguretat de les teves claus" + "Tanca sessió" + "Estàs a punt de tancar sessió a la teva última i única sessió. Si tanques sessió ara, perdràs l\'accés als missatges xifrats." + "Recuperació no configurada" + "Estàs a punt de tancar sessió. Si tanques sessió ara, pot ser que perdis l\'accés als missatges xifrats." + diff --git a/features/messages/impl/src/main/res/values-ca/translations.xml b/features/messages/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..f9b2685c7d --- /dev/null +++ b/features/messages/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,68 @@ + + + "El remitent de l\'esdeveniment no coincideix amb el propietari del dispositiu que l\'ha enviat." + "L\'autenticitat d\'aquest missatge xifrat en aquest dispositiu no es pot garantir." + "Xifrat per un usuari prèviament verificat." + "No xifrat." + "Xifrat per un dispositiu desconegut o eliminat." + "Xifrat per un dispositiu no verificat pel seu propietari." + "Xifrat per un usuari no verificat." + "Activitats" + "Banderes" + "Menjar & begudes" + "Animals i naturalesa" + "Objectes" + "Cares i persones" + "Viatges i llocs" + "Emoticones recents" + "Símbols" + "És possible que les llegendes no siguin visibles pels que utilitzin aplicacions antigues." + "No s\'ha pogut processar el contingut que s\'havia de pujar. Torna-ho a provar." + "No s\'ha pogut pujar el contingut. Torna-ho a provar." + "Bloqueja usuari" + "Marca si vols ocultar tots els missatges actuals i futurs d\'aquest usuari" + "Aquest missatge s\'enviarà a l\'administrador del teu servidor. No podrà llegir cap missatge xifrat." + "Motiu de la denúncia del contingut" + "Càmera" + "Fes una foto" + "Enregistra vídeo" + "Fitxer adjunt" + "Galeria de fotos i vídeos" + "Ubicació" + "Votació" + "Format de text" + "L\'historial de missatges no està disponible actualment." + "L\'historial de missatges no està disponible en aquesta sala. Verifica aquest dispositiu per veure l\'historial de missatges." + "Vols tornar-los a convidar-los?" + "No hi ha ningú més al xat" + "Notifica tota la sala" + "Tothom" + "Torna a enviar" + "No s\'han pogut enviar els missatges" + "Afegeix reacció" + "Aquest és el principi de %1$s." + "Principi d\'aquesta conversa." + "Trucada no compatible. Comprova si la persona que truca està utilitzant la nova aplicació Element X." + "Mostra\'n menys" + "Missatge copiat" + "No tens permís per enviar res en aquesta sala" + "Mostra\'n menys" + "Mostra\'n més" + "Nous" + + "%1$d canvi a la sala" + "%1$d canvis a la sala" + + "Aquesta sala ha estat substituïda i ja no està activa" + "Mostra els missatges antics" + "Aquesta sala és la continuació d\'una altra" + + "%1$s, %2$s i %3$d més" + "%1$s, %2$s i %3$d més" + + + "%1$s està escrivint" + "%1$s estan escrivint" + + "%1$s i %2$s" + diff --git a/features/messages/impl/src/main/res/values-ja/translations.xml b/features/messages/impl/src/main/res/values-ja/translations.xml index 037c0f7d67..93c10d13d8 100644 --- a/features/messages/impl/src/main/res/values-ja/translations.xml +++ b/features/messages/impl/src/main/res/values-ja/translations.xml @@ -26,6 +26,7 @@ "個数 %1$d / %2$d" "画像の品質を最適化" "処理中…" + "メディアを追加" "ユーザーをブロック" "このユーザーからのメッセージをすべて非表示にする場合はチェックしてください。" "このメッセージはホームサーバーの管理者に報告されます。暗号化されたメッセージを確認することはできません。" diff --git a/features/messages/impl/src/main/res/values-pl/translations.xml b/features/messages/impl/src/main/res/values-pl/translations.xml index efb84cd153..a0b9c24af2 100644 --- a/features/messages/impl/src/main/res/values-pl/translations.xml +++ b/features/messages/impl/src/main/res/values-pl/translations.xml @@ -26,6 +26,7 @@ "Pozycja %1$d z %2$d" "Zoptymalizuj jakość obrazu" "Przetwarzanie…" + "Dodaj media" "Zablokuj użytkownika" "Sprawdź, czy chcesz ukryć wszystkie bieżące i przyszłe wiadomości od tego użytkownika." "Ta wiadomość zostanie zgłoszona do administratora Twojego serwera domowego. Nie będzie mógł on przeczytać żadnych zaszyfrowanych wiadomości." diff --git a/features/messages/impl/src/main/res/values-zh/translations.xml b/features/messages/impl/src/main/res/values-zh/translations.xml index fc576aaf11..bdd898d26f 100644 --- a/features/messages/impl/src/main/res/values-zh/translations.xml +++ b/features/messages/impl/src/main/res/values-zh/translations.xml @@ -26,6 +26,7 @@ "第 %1$d 个项目,共 %2$d 个" "优化图像质量" "处理中…" + "添加媒体" "屏蔽用户" "请确认是否要隐藏该用户当前和未来的所有消息" "此消息将举报给服务器管理员。他们无法读取任何加密消息。" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index f5629f5e2d..9e0669d92c 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -26,6 +26,7 @@ "Item %1$d of %2$d" "Optimise image quality" "Processing…" + "Add media" "Block user" "Check if you want to hide all current and future messages from this user" "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." diff --git a/features/poll/impl/src/main/res/values-ca/translations.xml b/features/poll/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..21385e6473 --- /dev/null +++ b/features/poll/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,19 @@ + + + "Afegeix opció" + "Mostra els resultats només quan hagi finalitzat la votació" + "Amaga vots" + "Opció %1$d" + "Els canvis no s\'han desat. Segur que vols tornar enrere?" + "Pregunta o tema" + "De què tracta la votació?" + "Crea votació" + "Segur que vols eliminar la votació?" + "Elimina votació" + "Edita votació" + "No s\'han trobat votacions en curs." + "No s\'han trobat votacions passades." + "En curs" + "Pasades" + "Votacions" + diff --git a/features/preferences/impl/src/main/res/values-ca/translations.xml b/features/preferences/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..b2cb548d45 --- /dev/null +++ b/features/preferences/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,70 @@ + + + "Per assegurar que mai et perdis una trucada important, canvia la configuració per permetre les notificacions en pantalla completa quan el telèfon està bloquejat." + "Millora l\'experiència de les trucades" + "Tria com vols rebre les notificacions" + "Mode desenvolupador" + "Activa-ho per tenir accés a característiques i funcionalitats per a desenvolupadors." + "URL base d\'Element Call personalitzat" + "Estableix un URL base personalitzat d\'Element Call." + "URL invàlid, assegura\'t d\'incloure el protocol (http/https) i l\'adreça correctament." + "Amaga les previsualitzacions multimèdia a la cronologia" + "Puja fotos i vídeos més ràpidament i redueix l\'ús de dades" + "Optimitza qualitat de multimèdia" + "Moderació i seguretat" + "Proveïdor de notificacions push" + "Desactiva l\'editor de text enriquit per escriure Markdown manualment." + "Confirmacions de lectura" + "Si està desactivat, les confirmacions de lectura no s\'enviaran a ningú. Tot i així, rebràs les confirmacions de lectura d\'altres usuaris (que la tinguin activada)." + "Comparteix presència" + "Si està desactivat, no s\'enviaran ni rebràs confirmacions de lectura ni notificacions d\'escriptura." + "Amaga sempre" + "Mostra sempre" + "En sales privades" + "Els arxius multimèdia amagats es poden mostrar tocant-los." + "Mostra multimèdia a la cronologia" + "Activa l\'opció de mostrar el codi font dels missatges a la cronologia" + "No tens usuaris bloquejats" + "Desbloqueja" + "Podràs tornar a veure tots els seus missatges." + "Desbloqueja usuari" + "Desbloquejant…" + "Nom de visualització" + "El teu nom de visualització" + "S\'ha produït un error desconegut i no s\'ha pogut canviar la informació." + "No s\'ha pogut actualitzar el perfil" + "Edita perfil" + "Actualitzant perfil…" + "Configuració addicional" + "Trucades d\'àudio i vídeo" + "Configuració no coincident" + "S\'ha simplificat la configuració de notificacions per facilitar la cerca de les opcions. Algunes configuracions personalitzades anteriors no es mostren aquí, però encara estan actives. + +Si continues, és possible que alguna de les teves configuracions canviï." + "Xats directes" + "Configuració personalitzada per xat" + "S\'ha produït un error en actualitzar la configuració de notificacions." + "Tots els missatges" + "Mencions y paraules clau (només)" + "En xats directes, notifica\'m en" + "En xats de grup, notifica\'m en" + "Activa les notificacions en aquest dispositiu" + "La configuració no s\'ha corregit, torna-ho a intentar." + "Xats de grup" + "Invitacions" + "El servidor no admet aquesta opció en sales xifrades, és possible que no rebis notificacions en algunes sales." + "Mencions" + "Tot" + "Mencions" + "Notifica\'m a" + "Notifica\'m a @sala" + "Per rebre notificacions, canvia la teva %1$s." + "configuració del sistema" + "Notificacions del sistema desactivades" + "Notificacions" + "Fosc" + "Clar" + "Sistema" + "Soluciona problemes" + "Resolució de problemes de notificacions" + diff --git a/features/preferences/impl/src/main/res/values-fi/translations.xml b/features/preferences/impl/src/main/res/values-fi/translations.xml index 97da4e138a..99f900bcc1 100644 --- a/features/preferences/impl/src/main/res/values-fi/translations.xml +++ b/features/preferences/impl/src/main/res/values-fi/translations.xml @@ -11,6 +11,14 @@ "Piilota huoneiden avatarit kutsuista" "Piilota median esikatselu aikajanalla" "Labrat" + "Etäisyys, joka sinun on kuljettava päivityksen käynnistämiseksi." + "Varmista, että tälle sovellukselle on valittu \"Tarkka sijainti\". Voit muuttaa lupaa kohdassa %1$s." + "Sovellusasetukset" + "Reaaliaikaiset sijaintipäivitykset" + + "%1$d metrin välein" + "%1$d metrin välein" + "Lähetä valokuvia ja videoita nopeammin ja vähennä datan käyttöä." "Optimoi median laatu" "Moderointi ja Turvallisuus" @@ -72,10 +80,18 @@ Jos jatkat, jotkin asetukset saattavat muuttua." "Maininnat" "Ilmoita minulle" "Ilmoita minulle @room-maininnoista" + "Mukautettu ääni…" + "Virhe tiedoston poistamisessa" + "Elementin oletus" + "Virhe tiedoston tuonnissa" + "Ongelma ilmoitusäänen esikatselussa" + "Ääni" + "Ongelma ilmoitusäänen asettamisessa" "Jos haluat saada ilmoituksia, vaihda %1$s." "järjestelmäsi asetuksia" "Järjestelmän ilmoitukset on poissa päältä" "Ilmoitukset" + "Musta" "Tumma" "Vaalea" "Järjestelmän oletus" 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 3f97b67eb8..d7f17e4ffe 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -80,6 +80,7 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi "Mentions" "Prévenez-moi pour" "Prévenez-moi si un message contient \"@room\"" + "Son personnalisé…" "Pour recevoir des notifications, veuillez modifier votre %1$s." "paramètres du système" "Les notifications du système sont désactivées" diff --git a/features/preferences/impl/src/main/res/values-ja/translations.xml b/features/preferences/impl/src/main/res/values-ja/translations.xml index 95380cf148..5cab6bdcf8 100644 --- a/features/preferences/impl/src/main/res/values-ja/translations.xml +++ b/features/preferences/impl/src/main/res/values-ja/translations.xml @@ -79,10 +79,45 @@ "メンション" "以下を通知" @ルームで通知を受け取る + "カスタム着信音" + "ファイルの削除に失敗" + "Element の既定" + "Elementフェード" + "ファイルの取り込みに失敗" + "通知音のプレビューで問題が発生しました" + "音" + "通知音の設定に問題が発生しました" + "アラート" + "予感" + "ベル" + "開花" + "カリプソ" + "チャイム" + "汽車ポッポ" + "下降" + "エレクトロニック" + "ファンファーレ" + "ガラス" + "ホーン" + "はしご" + "メヌエット" + "速報" + "ノワール" + "シャーウッドの森" + "スペル" + "サスペンス" + "シュッ" + "電信" + "つま先" + "トリトーン" + "さえずり" + "タイプライター" + "アップデート" "通知を受け取るには、%1$s を変更してください。" "システム設定" "システムで通知がオフです" "通知" + "ブラック" "ダーク" "ライト" "システム" 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 83b02c6fe2..ca81984032 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -65,7 +65,7 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Czaty prywatne" - "Ustawienia własne wybranego czatu" + "Własne ustawienia dla wybranego czatu" "Wystąpił błąd podczas aktualizacji ustawienia powiadomień." "Wszystkie wiadomości" "Tylko wzmianki i słowa kluczowe" @@ -81,6 +81,40 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Wzmianki" "Powiadamiaj mnie przez" "Powiadom mnie na @pokój" + "Własny dźwięk…" + "Błąd usuwania pliku" + "Domyślny Element" + "Element Fade" + "Błąd importowania pliku" + "Wystąpił błąd podczas podglądania dźwięku powiadomień" + "Dźwięk" + "Nie udało się ustawić dźwięku powiadomień" + "Alert" + "Oczekiwanie" + "Dzwonek" + "Rozkwit" + "Kalipso" + "Gong" + "Ciuchcia" + "Opadający" + "Elektroniczna" + "Fanfary" + "Szkło" + "Róg" + "Drabina" + "Minuet" + "Wiadomości" + "Kryminał" + "Las Sherwood" + "Zaklęcie" + "Napięcie" + "Świst" + "Telegraf" + "Na palcach" + "Trójdźwięk" + "Tweet" + "Maszyna do pisania" + "Aktualizacja" "Aby otrzymywać powiadomienia, zmień swoje%1$s." "ustawienia systemowe" "Powiadomienia systemowe wyłączone" 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 edb98feee8..eb4ad2245d 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -11,6 +11,15 @@ "Скрывать аватары в приглашениях" "Скрывать предпросмотр медиа в истории сообщений" "Лаборатория" + "Расстояние, которое нужно пройти, чтобы инициировать обновление." + "Убедитесь, что для этого приложения включена функция «Точное местоположение». Чтобы изменить разрешение, перейдите на страницу %1$s." + "Настройки приложения" + "Обновления местоположения в режиме реального времени" + + "Каждый %1$d метр" + "Каждые %1$d метра" + "Каждые %1$d метров" + "Загружайте фотографии и видео быстрее и сокращайте потребление трафика" "Оптимизировать качество мультимедиа" "Модерация и безопасность" @@ -76,6 +85,7 @@ "системные настройки" "Системные уведомления выключены" "Уведомления" + "Черный" "Темная" "Светлое" "Системное" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index 8f6de8209e..3726b82206 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -79,6 +79,40 @@ "提及" "通知我以下类型" "提及所有成员(@room)时通知我" + "自定义声音…" + "删除文件时出错" + "Element 默认" + "Element 淡入" + "导入文件时出错" + "预览提示音时出现问题" + "声音" + "设置提示音时出现问题" + "提醒声" + "悉心期盼" + "铃铛" + "百花怒放" + "即兴曲调" + "风铃" + "火车鸣笛" + "渐弱" + "电子乐" + "嘹亮吹奏声" + "玻璃声" + "圆号" + "连音效果" + "小步舞曲" + "新闻快讯" + "夜色" + "绿林好汉的旋律" + "神奇魔咒" + "侦探悬念" + "嗖嗖声" + "敲打电报" + "蹑手蹑脚" + "三全音" + "鸟鸣声" + "敲打字机" + "整点新闻" "要接收通知,请更改 %1$s。" "系统设置" "系统通知已关闭" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index a431139fd6..7389a0433c 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -80,6 +80,40 @@ If you proceed, some of your settings may change." "Mentions" "Notify me for" "Notify me on @room" + "Custom sound…" + "Error deleting file" + "Element Default" + "Element Fade" + "Error importing file" + "Problem previewing alert sound" + "Sound" + "Problem setting alert sound" + "Alert" + "Anticipate" + "Bell" + "Bloom" + "Calypso" + "Chime" + "Choo Choo" + "Descent" + "Electronic" + "Fanfare" + "Glass" + "Horn" + "Ladder" + "Minuet" + "News Flash" + "Noir" + "Sherwood Forest" + "Spell" + "Suspense" + "Swish" + "Telegraph" + "Tiptoes" + "Tri-tone" + "Tweet" + "Typewriters" + "Update" "To receive notifications, please change your %1$s." "system settings" "System notifications turned off" diff --git a/features/rageshake/api/src/main/res/values-ca/translations.xml b/features/rageshake/api/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..b5907369e6 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "%1$s ha fallat l\'últim cop que es va utilitzar. Vols enviar un informe d\'errors?" + "Sembla que sacseges el telèfon amb frustració. Vols obrir la pantalla d\'informe d\'errors?" + "La sacsejada de frustració és quan mous i sacseges el mòbil per informar d\'un error. L\'aplicació ja no és compatible amb aquesta funcionalitat però el nom encara s\'utilitza per raons històriques." + "Llindar de detecció" + diff --git a/features/rageshake/impl/src/main/res/values-ca/translations.xml b/features/rageshake/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..6586cd9dc2 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,17 @@ + + + "Adjunta captura de pantalla" + "Pots contactar amb mi si tens comentaris addicionals." + "Contacta\'m" + "Edita captura de pantalla" + "Descriu el problema. Què has fet? Què esperaves que passés? Què va has passat realment? Si us plau, explica-ho el més detalladament possible." + "Descriu el problema…" + "Si és possible, escriu la descripció en anglès." + "Descripció massa curta. Si us plau, proporciona més detalls sobre què ha passat. Gràcies!" + "Envia registre de fallada" + "Permet registres" + "Envia captura de pantalla" + "Els registres s\'inclouran amb el missatge per intentar resoldre el problema correctament. Per enviar el missatge sense registres, desactiva aquesta opció." + "%1$s ha fallat l\'últim cop que es va utilitzar. Vols enviar un informe d\'errors?" + "Veure registres" + diff --git a/features/reportroom/impl/src/main/res/values-ca/translations.xml b/features/reportroom/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..05ca5c0fc0 --- /dev/null +++ b/features/reportroom/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,6 @@ + + + "Denuncia aquesta sala al teu administrador. Si els missatges estan xifrats, l\'administrador no els podrà llegir." + "Descriu el motiu de la denúncia…" + "Denuncia sala" + diff --git a/features/rolesandpermissions/impl/src/main/res/values-ca/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..eccea6f2df --- /dev/null +++ b/features/rolesandpermissions/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,61 @@ + + + "Només administradors" + "Bandejar usuaris" + "Eliminar missatges" + "Membre" + "Convidar persones i acceptar sol·licituds d\'unió" + "Gestiona membres" + "Missatges i contingut" + "Administradors i moderadors" + "Eliminar persones i rebutjar sol·licituds d\'unió" + "Canvia la foto de la sala" + "Edita detalls" + "Canvia el nom de la sala" + "Canvia el tema de la sala" + "Enviar missatges" + "Edita administradors" + "No podràs desfer aquesta acció. Estàs concedint a l\'usuari el mateix nivell de poder que tu." + "Afegir com a administrador?" + "Descendeix" + "No podràs desfer aquest canvi ja que t\'estàs descendint de rang, si ets l\'últim usuari de la sala amb privilegis, no podràs recuperar-los." + "Vols descendir de categoria?" + "%1$s (pendent)" + "(pendent)" + "Els administradors tenen automàticament privilegis de moderador" + "Edita moderadors" + "Administradors" + "Moderadors" + "Membres" + "Hi ha canvis sense desar." + "Desar canvis?" + "No hi ha usuaris bandejats en aquesta sala." + + "%1$d persona" + "%1$d persones" + + "Bandeja de la sala" + "Només elimina\'l" + "Desbandeja" + "Podran tornar a unir-se a aquesta sala si se\'ls convida." + "Readmet usuari" + "Bandejats" + "Membres" + "Només administradors" + "Administradors i moderadors" + "Membres de la sala" + "Readmetent %1$s" + "Administradors" + "Canvia el meu rol" + "Descendeix a membre" + "Descendeix a moderador" + "Moderació de membres" + "Missatges i contingut" + "Moderadors" + "Restableix permisos" + "Si restableixes els permisos, perdràs la configuració actual." + "Restablir permisos?" + "Rols" + "Detalls de sala" + "Rols i permisos" + diff --git a/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml index 7c5d635b14..fdc1c17f22 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-fi/translations.xml @@ -6,6 +6,7 @@ "Viestien poistaminen" "Jäsen" "Kutsujen antaminen" + "Jaa reaaliaikainen sijainti" "Tilan hallitseminen" "Huoneiden hallitseminen" "Jäsenien hallitseminen" diff --git a/features/rolesandpermissions/impl/src/main/res/values-ja/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ja/translations.xml index c9410e9e3c..28e1a9db89 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-ja/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-ja/translations.xml @@ -6,6 +6,7 @@ "メッセージの削除" "メンバー" "ユーザーの招待" + "ライブ位置情報を共有" "スペースの管理" "ルームを管理" "メンバーの管理" diff --git a/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml index 2b7fca0254..425da3e2ce 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-ru/translations.xml @@ -6,6 +6,7 @@ "Удалять сообщения" "Участники" "Приглашать людей" + "Поделиться текущим местоположением" "Управление пространством" "Управление комнатами" "Управлять участниками" diff --git a/features/roomaliasresolver/impl/src/main/res/values-ca/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..ea90cabc34 --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "No s\'ha pogut mostrar la vista prèvia de la sala" + "No s\'ha pogut obtenir l\'àlies de sala." + diff --git a/features/roomdetails/impl/src/main/res/values-be/translations.xml b/features/roomdetails/impl/src/main/res/values-be/translations.xml index 5bd33fef17..69c5be0021 100644 --- a/features/roomdetails/impl/src/main/res/values-be/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-be/translations.xml @@ -100,4 +100,6 @@ "Ролі" "Дэталі пакоя" "Ролі і дазволы" + "Пазначыць як прачытанае" + "Пазначыць як непрачытанае" diff --git a/features/roomdetails/impl/src/main/res/values-bg/translations.xml b/features/roomdetails/impl/src/main/res/values-bg/translations.xml index 2c797cb74e..7369d78f1d 100644 --- a/features/roomdetails/impl/src/main/res/values-bg/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-bg/translations.xml @@ -79,6 +79,8 @@ "Роли" "Подробности за стаята" "Роли и разрешения" + "Отбелязване като прочетено" + "Отбелязване като непрочетено" "Добавяне на адрес" "Да, включване на шифроването" "Да се включи ли шифроването?" diff --git a/features/roomdetails/impl/src/main/res/values-ca/translations.xml b/features/roomdetails/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..8b9f8d937e --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,141 @@ + + + "És necessària una adreça perquè sigui visible al directori públic." + "Edita adreça" + "S\'ha produït un error en actualitzar la configuració de notificacions." + "El servidor no admet aquesta opció en sales xifrades, és possible que no rebis notificacions en algunes sales." + "Votacions" + "Només administradors" + "Bandejar usuaris" + "Eliminar missatges" + "Membre" + "Convidar persones i acceptar sol·licituds d\'unió" + "Gestiona membres" + "Missatges i contingut" + "Administradors i moderadors" + "Eliminar persones i rebutjar sol·licituds d\'unió" + "Canvia la foto de la sala" + "Edita detalls" + "Canvia el nom de la sala" + "Canvia el tema de la sala" + "Enviar missatges" + "Edita administradors" + "No podràs desfer aquesta acció. Estàs concedint a l\'usuari el mateix nivell de poder que tu." + "Afegir com a administrador?" + "Descendeix" + "No podràs desfer aquest canvi ja que t\'estàs descendint de rang, si ets l\'últim usuari de la sala amb privilegis, no podràs recuperar-los." + "Vols descendir de categoria?" + "%1$s (pendent)" + "(pendent)" + "Els administradors tenen automàticament privilegis de moderador" + "Edita moderadors" + "Administradors" + "Moderadors" + "Membres" + "Hi ha canvis sense desar." + "Desar canvis?" + "Afegeix tema" + "Xifrada" + "No xifrada" + "Sala pública" + "Edita detalls" + "Un error desconegut ha impedit l\'intercanvi d\'informació." + "No s\'ha pogut actualitzar la sala" + "Els missatges estan protegits amb cadenats. Només tu i els destinataris teniu les úniques claus per a desbloquejar-los." + "Xifrat de missatges activat" + "S\'ha produït un error en carregar la configuració de notificacions." + "No s\'ha pogut silenciar la sala. Torna-ho a intentar." + "No s\'ha pogut deixar de silenciar la sala. Torna-ho a provar." + "Convida persones" + "Convida" + "Surt del xat" + "Surt de la sala" + "Multimèdia i documents" + "Personalitzat" + "Predeterminat" + "Notificacions" + "Missatges fixats" + "Perfil" + "Sol·licituds d\'unió" + "Rols i permisos" + "Seguretat i privadesa" + "Seguretat" + "Comparteix sala" + "Informació de sala" + "Tema" + "Actualitzant sala…" + "No hi ha usuaris bandejats en aquesta sala." + + "%1$d persona" + "%1$d persones" + + "Bandeja de la sala" + "Només elimina\'l" + "Desbandeja" + "Podran tornar a unir-se a aquesta sala si se\'ls convida." + "Readmet usuari" + "Bandejats" + "Membres" + "Només administradors" + "Administradors i moderadors" + "Membres de la sala" + "Readmetent %1$s" + "Permet la configuració personalitzada" + "Si ho actives, se substituirà la configuració predeterminada" + "Notifica\'m en aquest xat" + "Ho pots canviar al la %1$s." + "configuració global" + "Configuració predeterminada" + "Elimina la configuració personalitzada" + "S\'ha produït un error en carregar la configuració de notificacions." + "No s\'ha pogut restaurar el mode predeterminat. Torna-ho a provar." + "No s\'ha pogut configurar el mode. Torna-ho a provar." + "El servidor no admet aquesta opció en sales xifrades, no rebras notificacions en aquesta sala." + "Tots els missatges" + "Mencions y paraules clau (només)" + "En aquesta sala, notifica\'m en" + "Administradors" + "Canvia el meu rol" + "Descendeix a membre" + "Descendeix a moderador" + "Moderació de membres" + "Missatges i contingut" + "Moderadors" + "Restableix permisos" + "Si restableixes els permisos, perdràs la configuració actual." + "Restablir permisos?" + "Rols" + "Detalls de sala" + "Rols i permisos" + "Marca com a llegit" + "Marca com a no llegit" + "Afegeix adreça" + "Tothom ha de sol·licitar l\'accés." + "Sol·licita unir-t\'hi" + "Sí, activa el xifrat" + "Un cop activat, el xifrat d\'una sala no es pot desactivar. L\'històric de missatges només serà visible per als membres de la sala des que van ser convidats o des que s\'hi van unir. +Ningú a part dels membres de la sala podrà llegir els missatges. Això pot impedir que els bots i els ponts (\'bridges\') funcionin correctament. +No es recomana activar el xifrat a les sales que tothom pot trobar i unir-se." + "Vols activar el xifrat?" + "Un cop activat, el xifrat no es pot desactivar." + "Xifrat" + "Activa el xifrat d\'extrem a extrem" + "Tothom pot unir-s\'hi." + "Tothom" + "Només s\'hi poden unir les persones convidades." + "Només amb invitació" + "Accés" + "Actualment els espais no són compatibles" + "És necessària una adreça perquè sigui visible al directori públic." + "Adreça" + "Permet trobar aquesta sala cercant %1$s al directori públic de sales" + "Visible al directori públic" + "Tothom (historial públic)" + "Qui pot llegir l\'historial?" + "Membres, des de quan es van convidar" + "Membres (historial complet)" + "Les adreces de sala són maneres de trobar i accedir a les sales. Això també garanteix que puguis compartir fàcilment la teva sala amb altres persones. +Pots optar per publicar la teva sala al directori públic de sales del teu servidor local." + "Visibilitat" + "Seguretat i privadesa" + diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml index d47a1c709b..190e1ae5de 100644 --- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -135,6 +135,8 @@ "Role" "Podrobnosti místnosti" "Role a oprávnění" + "Označit jako přečtené" + "Označit jako nepřečtené" "Přidat adresu" "Připojit se může kdokoli v autorizovaných prostorách, ale všichni ostatní musí o přístup požádat." "Všichni musí požádat o přístup." diff --git a/features/roomdetails/impl/src/main/res/values-cy/translations.xml b/features/roomdetails/impl/src/main/res/values-cy/translations.xml index b8ec88e51a..f5d4eecac5 100644 --- a/features/roomdetails/impl/src/main/res/values-cy/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cy/translations.xml @@ -120,6 +120,8 @@ "Rolau" "Manylion yr ystafell" "Rolau a chaniatâd" + "Marcio fel wedi\'i ddarllen" + "Marcio fel heb ei ddarllen" "Ychwanegu cyfeiriad ystafell" "Gall unrhyw un ofyn am gael ymuno â\'r ystafell ond bydd rhaid i weinyddwr neu gymedrolwr dderbyn y cais." "Gofyn i gael ymuno" diff --git a/features/roomdetails/impl/src/main/res/values-da/translations.xml b/features/roomdetails/impl/src/main/res/values-da/translations.xml index 9f3b3436f0..191df9f059 100644 --- a/features/roomdetails/impl/src/main/res/values-da/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-da/translations.xml @@ -132,6 +132,8 @@ "Roller" "Detaljer om rummet" "Roller og tilladelser" + "Marker som læst" + "Marker som ulæst" "Tilføj adresse" "Alle i autoriserede klynger kan deltage, men alle andre skal anmode om adgang." "Alle skal anmode om adgang." diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index c1487d7d98..08201fdc34 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -132,6 +132,8 @@ "Rollen" "Chat-Details anpassen" "Rollen und Berechtigungen" + "Als gelesen markieren" + "Als ungelesen markieren" "Chat-Adresse hinzufügen" "Jedes Mitglied eines autorisierten Space kann beitreten, aber alle anderen müssen einen Beitritt anfragen." "Zugang nur auf Anfrage." diff --git a/features/roomdetails/impl/src/main/res/values-el/translations.xml b/features/roomdetails/impl/src/main/res/values-el/translations.xml index c1be20d7f5..5f1a3ce4ab 100644 --- a/features/roomdetails/impl/src/main/res/values-el/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-el/translations.xml @@ -132,6 +132,8 @@ "Ρόλοι" "Λεπτομέρειες αίθουσας" "Ρόλοι και δικαιώματα" + "Επισήμανση ως αναγνωσμένου" + "Επισήμανση ως μη αναγνωσμένου" "Προσθήκη διεύθυνσης" "Οποιοσδήποτε σε εξουσιοδοτημένους χώρους μπορεί να συμμετάσχει, αλλά όλοι οι άλλοι πρέπει να ζητήσουν πρόσβαση." "Όλοι πρέπει να αιτούνται πρόσβαση." diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index 6b92e48884..9537596d4e 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -107,6 +107,8 @@ "Roles" "Detalles de la sala" "Roles y permisos" + "Marcar como leído" + "Marcar como no leído" "Agregar dirección" "Todos deben solicitar acceso." "Solicitar unirse" diff --git a/features/roomdetails/impl/src/main/res/values-et/translations.xml b/features/roomdetails/impl/src/main/res/values-et/translations.xml index f0c8c58b1f..e6962456ac 100644 --- a/features/roomdetails/impl/src/main/res/values-et/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-et/translations.xml @@ -132,6 +132,8 @@ "Rollid" "Jututoa üksikasjad" "Rollid ja õigused" + "Märgi loetuks" + "Märgi mitteloetuks" "Lisa aadress" "Liituda saavad kõik volitatud kogukondade liikmed, kuid kõik teised peavad küsima võimalust ligipääsuks." "Kõik võivad paluda jututoaga liitumist." diff --git a/features/roomdetails/impl/src/main/res/values-eu/translations.xml b/features/roomdetails/impl/src/main/res/values-eu/translations.xml index f299131852..64f32e44d9 100644 --- a/features/roomdetails/impl/src/main/res/values-eu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-eu/translations.xml @@ -100,6 +100,8 @@ "Rolak" "Gelaren xehetasunak" "Rolak eta baimenak" + "Markatu irakurritzat" + "Markatu irakurri gabetzat" "Gehitu gelaren helbidea" "Bai, gaitu zifratzea" "Zifratzea" diff --git a/features/roomdetails/impl/src/main/res/values-fa/translations.xml b/features/roomdetails/impl/src/main/res/values-fa/translations.xml index bb7cb490b8..af0cbbe29b 100644 --- a/features/roomdetails/impl/src/main/res/values-fa/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fa/translations.xml @@ -113,6 +113,8 @@ "نقش‌ها" "جزییات اتاق" "نقش‌ها و مجوزها" + "علامت‌گذاری به عنوان خوانده شده" + "نشان به ناخوانده" "افزودن نشانی اتاق" "درخواست دعوت" "بله. به کار انداختن رمزنگاری" diff --git a/features/roomdetails/impl/src/main/res/values-fi/translations.xml b/features/roomdetails/impl/src/main/res/values-fi/translations.xml index 0d07ff4eae..4eafca087e 100644 --- a/features/roomdetails/impl/src/main/res/values-fi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fi/translations.xml @@ -132,6 +132,8 @@ "Roolit" "Huoneen tiedot" "Roolit ja oikeudet" + "Merkitse luetuksi" + "Merkitse lukemattomaksi" "Lisää osoite" "Kuka tahansa valtuutetuissa tiloissa voi liittyä, mutta kaikkien muiden on pyydettävä pääsyä." "Kaikkien on pyydettävä pääsyä." diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index 3d4283145c..0d6844cb7e 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -132,6 +132,8 @@ "Rôles" "Détails du salon" "Rôles & autorisations" + "Marquer comme lu" + "Marquer comme non lu" "Ajouter une adresse" "Toute personne se trouvant dans un espace autorisé peut participer, mais toutes les autres doivent demander l’accès." "Tout le monde doit demander un accès." diff --git a/features/roomdetails/impl/src/main/res/values-hr/translations.xml b/features/roomdetails/impl/src/main/res/values-hr/translations.xml index 73b6866249..5619164dd2 100644 --- a/features/roomdetails/impl/src/main/res/values-hr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hr/translations.xml @@ -135,6 +135,8 @@ "Uloge" "Pojedinosti o sobi" "Uloge i dopuštenja" + "Označi kao pročitano" + "Označi kao nepročitano" "Dodaj adresu" "Svatko tko se nalazi u ovlaštenim prostorima može se pridružiti, ali svi ostali moraju zatražiti pristup." "Svi moraju zatražiti pristup." 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 937c91b68d..fed765fb45 100644 --- a/features/roomdetails/impl/src/main/res/values-hu/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-hu/translations.xml @@ -132,6 +132,8 @@ "Szerepkörök" "Szoba részletei" "Szerepkörök és jogosultságok" + "Megjelölés olvasottként" + "Megjelölés olvasatlanként" "Cím hozzáadása" "Bárki csatlakozhat, az engedélyezett terekből, és mindenki másnak hozzáférést kell kérnie." "Mindenkinek hozzáférést kell kérnie." diff --git a/features/roomdetails/impl/src/main/res/values-in/translations.xml b/features/roomdetails/impl/src/main/res/values-in/translations.xml index 2cce124321..d7403329c4 100644 --- a/features/roomdetails/impl/src/main/res/values-in/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-in/translations.xml @@ -115,6 +115,8 @@ "Peran" "Detail ruangan" "Peran dan perizinan" + "Tandai sebagai dibaca" + "Tandai sebagai belum dibaca" "Tambahkan alamat" "Meminta hak akses pada administrator atau moderator." "Ijin bergabung" diff --git a/features/roomdetails/impl/src/main/res/values-it/translations.xml b/features/roomdetails/impl/src/main/res/values-it/translations.xml index 044721c345..b8a18d4421 100644 --- a/features/roomdetails/impl/src/main/res/values-it/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-it/translations.xml @@ -132,6 +132,8 @@ "Ruoli" "Dettagli della stanza" "Ruoli e autorizzazioni" + "Segna come letto" + "Segna come non letto" "Aggiungi indirizzo" "Chiunque si trovi in spazi autorizzati può partecipare, ma tutti gli altri devono richiedere l\'accesso." "Chiunque deve richiedere l\'accesso." diff --git a/features/roomdetails/impl/src/main/res/values-ja/translations.xml b/features/roomdetails/impl/src/main/res/values-ja/translations.xml index b6c7608793..5a65f1a404 100644 --- a/features/roomdetails/impl/src/main/res/values-ja/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ja/translations.xml @@ -129,6 +129,8 @@ "役割" "ルームの詳細" "役割と権限" + "既読にする" + "未読にする" "アドレスを追加" "認証済みのスペースに所属するユーザーのみが参加できます。それ以外のユーザーは参加へのリクエストが必要です。" "参加のリクエストが必須です。" diff --git a/features/roomdetails/impl/src/main/res/values-ka/translations.xml b/features/roomdetails/impl/src/main/res/values-ka/translations.xml index 317f62313d..8fd0b2c8a6 100644 --- a/features/roomdetails/impl/src/main/res/values-ka/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ka/translations.xml @@ -94,4 +94,6 @@ "როლები" "ოთახის დეტალები" "როლები და ნებართვები" + "წაკითხულად მონიშვნა" + "წაუკითხავად მონიშვნა" diff --git a/features/roomdetails/impl/src/main/res/values-ko/translations.xml b/features/roomdetails/impl/src/main/res/values-ko/translations.xml index 8c0db6eefd..4a32cab45d 100644 --- a/features/roomdetails/impl/src/main/res/values-ko/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ko/translations.xml @@ -129,6 +129,8 @@ "역할" "방 세부 정보" "역할 및 권한" + "읽음으로 표시" + "읽지 않음으로 표시" "주소 추가" "승인된 스페이스의 멤버는 누구나 참여할 수 있지만, 그 외의 인원은 액세스 요청을 해야 합니다." "모든 사용자가 액세스 권한을 요청해야 합니다." diff --git a/features/roomdetails/impl/src/main/res/values-nb/translations.xml b/features/roomdetails/impl/src/main/res/values-nb/translations.xml index 52ea87bc3d..0cb3dfc268 100644 --- a/features/roomdetails/impl/src/main/res/values-nb/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nb/translations.xml @@ -132,6 +132,8 @@ "Roller" "Romdetaljer" "Roller og tillatelser" + "Marker som lest" + "Merk som ulest" "Legg til adresse" "Alle i autoriserte områder kan bli med, men alle andre må be om tilgang." "Alle må be om tilgang." diff --git a/features/roomdetails/impl/src/main/res/values-nl/translations.xml b/features/roomdetails/impl/src/main/res/values-nl/translations.xml index e9f03d3a57..2ee5caca97 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -100,4 +100,6 @@ "Rollen" "Kamergegevens" "Rollen en rechten" + "Markeren als gelezen" + "Markeren als ongelezen" diff --git a/features/roomdetails/impl/src/main/res/values-pl/translations.xml b/features/roomdetails/impl/src/main/res/values-pl/translations.xml index ccc359ed25..2979fcacdf 100644 --- a/features/roomdetails/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pl/translations.xml @@ -135,6 +135,8 @@ "Role" "Szczegóły pokoju" "Role i uprawnienia" + "Oznacz jako przeczytane" + "Oznacz jako nieprzeczytane" "Dodaj adres" "Każdy w autoryzowanych przestrzeniach może dołączyć, ale wszyscy inni muszą poprosić o dostęp." "Każdy musi poprosić o dostęp." diff --git a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml index da98e7bd5a..f2ddfb86cb 100644 --- a/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt-rBR/translations.xml @@ -129,6 +129,8 @@ "Cargos" "Detalhes da sala" "Cargos e permissões" + "Marcar como lida" + "Marcar como não lida" "Adicionar endereço" "Qualquer um nos espaços autorizados podem entrar, mas todos os outros devem pedir acesso." "Qualquer um pode pedir acesso, mas um administrador terá que aceitar o pedido." diff --git a/features/roomdetails/impl/src/main/res/values-pt/translations.xml b/features/roomdetails/impl/src/main/res/values-pt/translations.xml index c5a9be9a4c..04ded84480 100644 --- a/features/roomdetails/impl/src/main/res/values-pt/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pt/translations.xml @@ -128,6 +128,8 @@ "Cargos" "Detalhes da sala" "Cargos e permissões" + "Marcar como lida" + "Marcar como não lida" "Adicionar endereço" "Todos precisam de pedir acesso." "Pedir para entrar" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index 425e35a23c..f3feda811a 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -135,6 +135,8 @@ "Roluri" "Detaliile camerei" "Roluri și permisiuni" + "Marcați ca citită" + "Marcați ca necitită" "Adăugați o adresă" "Oricine se află în spațiile autorizate se poate alătura, dar toți ceilalți trebuie să solicite accesul." "Toată lumea trebuie să solicite acces." diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index bd683009ad..bb36b3b23b 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -135,6 +135,8 @@ "Роли" "Информация о комнате" "Роли и разрешения" + "Пометить как прочитанное" + "Отметить как непрочитанное" "Добавить адрес" "Кто угодно из авторизованных пространств может присоединиться, а всем остальным необходимо запросить доступ." "Каждый должен запросить доступ." diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index 6a27e375ee..122c27e058 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -132,6 +132,8 @@ "Roly" "Podrobnosti o miestnosti" "Roly a povolenia" + "Označiť ako prečítané" + "Označiť ako neprečítané" "Pridať adresu" "Pripojiť sa môže ktokoľvek z autorizovaných priestorov, ale všetci ostatní musia o prístup požiadať." "Všetci musia požiadať o prístup." diff --git a/features/roomdetails/impl/src/main/res/values-sv/translations.xml b/features/roomdetails/impl/src/main/res/values-sv/translations.xml index 02574e77e9..aa9f244498 100644 --- a/features/roomdetails/impl/src/main/res/values-sv/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sv/translations.xml @@ -116,6 +116,8 @@ "Roller" "Rumsdetaljer" "Roller och behörigheter" + "Markera som läst" + "Markera som oläst" "Lägg till adress" "Alla måste begära åtkomst." "Be om att gå med" diff --git a/features/roomdetails/impl/src/main/res/values-tr/translations.xml b/features/roomdetails/impl/src/main/res/values-tr/translations.xml index 8d11b8035a..a9bfc6fbe2 100644 --- a/features/roomdetails/impl/src/main/res/values-tr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-tr/translations.xml @@ -113,6 +113,8 @@ "Roller" "Oda bilgileri" "Roller ve izinler" + "Okundu olarak işaretle" + "Okunmamış olarak işaretle" "Oda adresi ekle" "Yetkilendirilmiş alanlardaki herkes katılabilir, diğer herkes erişim talep etmelidir." "Herkes odaya katılma isteğinde bulunabilir ancak bir yönetici veya moderatörün isteği kabul etmesi gerekir." diff --git a/features/roomdetails/impl/src/main/res/values-uk/translations.xml b/features/roomdetails/impl/src/main/res/values-uk/translations.xml index 1565e0ec48..8f566bb235 100644 --- a/features/roomdetails/impl/src/main/res/values-uk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uk/translations.xml @@ -135,6 +135,8 @@ "Ролі" "Деталі кімнати" "Ролі та дозволи" + "Позначити прочитаним" + "Позначити непрочитаним" "Додати адресу" "Будь-хто в авторизованих просторах може приєднатися, але всі інші повинні подати запит на доступ." "Усі повинні запитувати доступ." diff --git a/features/roomdetails/impl/src/main/res/values-ur/translations.xml b/features/roomdetails/impl/src/main/res/values-ur/translations.xml index d5ab09dd18..5c32811f36 100644 --- a/features/roomdetails/impl/src/main/res/values-ur/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ur/translations.xml @@ -99,4 +99,6 @@ "کردارہا" "کمرے کی تفصیلات" "کردارہا اور اجازتیں" + "بطور مقروءہ نشانزد کریں" + "بطور غیر مقروءہ نشانزد کریں" diff --git a/features/roomdetails/impl/src/main/res/values-uz/translations.xml b/features/roomdetails/impl/src/main/res/values-uz/translations.xml index fa9408b3ba..d7778f2df4 100644 --- a/features/roomdetails/impl/src/main/res/values-uz/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uz/translations.xml @@ -132,6 +132,8 @@ "Rollar" "Xona tafsilotlari" "Rollar va ruxsatlar" + "Oʻqilgan deb belgilash" + "Oʻqilmagan deb belgilash" "Xona manzilini kiritish" "Vakolatli guruhlardagi har kim qo‘shilishi mumkin, lekin qolganlar ruxsat so‘rashi kerak. Tarjima eslatmasi yo‘q" "Xonaga qo‘shilishni istalgan kishi so‘rashi mumkin, lekin administrator yoki moderator so‘rovni qabul qilishi kerak" diff --git a/features/roomdetails/impl/src/main/res/values-vi/translations.xml b/features/roomdetails/impl/src/main/res/values-vi/translations.xml index 5e25014c88..ecc7e4e227 100644 --- a/features/roomdetails/impl/src/main/res/values-vi/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-vi/translations.xml @@ -106,6 +106,8 @@ "Vai trò" "Chi tiết phòng" "Vai trò và quyền hạn" + "Đánh dấu đã đọc" + "Đánh dấu chưa đọc" "Sau khi được kích hoạt, mã hóa cho một phòng chat không thể tắt được. Lịch sử tin nhắn chỉ hiển thị cho các thành viên phòng chat kể từ khi họ được mời hoặc kể từ khi họ tham gia phòng chat. Không ai ngoài các thành viên phòng chat có thể đọc tin nhắn. Điều này có thể ngăn chặn bot và các thiết bị kết nối hoạt động đúng cách. Chúng tôi không khuyến khích bật mã hóa cho các phòng chat mà bất kỳ ai cũng có thể tìm thấy và tham gia." diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml index 04ccc56adb..0e516d0c29 100644 --- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -129,6 +129,8 @@ "身份" "聊天室資訊" "角色與權限" + "標為已讀" + "標為未讀" "新增地址" "任何在授權空間的人都可以加入,但其他人都必須提出申請。" "所有人都必須申請存取權。" diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index 1fef5583a3..8a5c5734ed 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -129,6 +129,8 @@ "角色" "房间详细信息" "角色与权限" + "设为已读" + "设为未读" "添加地址" "已授权空间内的任何成员都可以加入,其他人必须申请访问。" "所有用户均需申请访问权限。" diff --git a/features/roomdetailsedit/impl/src/main/res/values-ca/translations.xml b/features/roomdetailsedit/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..8be6967ace --- /dev/null +++ b/features/roomdetailsedit/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Edita detalls" + "Un error desconegut ha impedit l\'intercanvi d\'informació." + "No s\'ha pogut actualitzar la sala" + "Actualitzant sala…" + diff --git a/features/roomdirectory/impl/src/main/res/values-ca/translations.xml b/features/roomdirectory/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..fc664290c1 --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "No s\'ha pogut carregar" + "Directori de sales" + diff --git a/features/roommembermoderation/impl/src/main/res/values-ca/translations.xml b/features/roommembermoderation/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..9495112c4e --- /dev/null +++ b/features/roommembermoderation/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,20 @@ + + + "Bandeja de la sala" + "Bandeja" + "No podrà tornar a unir-se a aquesta sala encara que se\'l convidi." + "Segur que vols bandejar aquest membre?" + "Bandejant %1$s" + "Elimina" + "Podran tornar a unir-se a aquesta sala si se\'ls convida." + "Segur que vols eliminar aquest membre?" + "Veure perfil" + "Elimina de la sala" + "Vols eliminar l\'usuari i prohibir-li l\'accés en el futur?" + "Eliminant %1$s…" + "Readmet usuari" + "Desbandeja" + "Podrà tornar a unir-se a través d\'una invitació" + "Segur que vols readmetre aquest membre?" + "Readmetent %1$s" + diff --git a/features/securebackup/impl/src/main/res/values-ca/translations.xml b/features/securebackup/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..0ca99788ed --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,69 @@ + + + "Elimina l\'emmagatzematge de claus" + "Activa còpia de seguretat" + "Emmagatzema la teva identitat criptogràfica i les claus de missatge de forma segura al servidor. Això et permetrà veure l\'historial de missatges en qualsevol dispositiu nou. %1$s." + "Emmagatzematge de claus" + "L\'emmagatzematge de claus ha d\'estar activat per poder configurar la recuperació." + "Puja claus des d\'aquest dispositiu" + "Permet l\'emmagatzematge de claus" + "Canvia la clau de recuperació" + "Recupera la teva identitat criptogràfica i l\'historial de missatges amb una clau de recuperació si has perdut tots els teus dispositius existents." + "Introdueix clau de recuperació" + "L\'emmagatzematge de claus no està sincronitzat." + "Configura la recuperació" + "Obre %1$s a un dispositiu d\'escriptori" + "Torna a iniciar sessió" + "Quan se\'t demani verificar el dispositiu, selecciona %1$s" + "“Restableix-ho tot”" + "Segueix les instruccions per crear una nova clau de recuperació" + "Desa la nova clau de recuperació en un gestor de contrasenyes o en una nota xifrada" + "Restableix el xifrat del teu compte mitjançant un altre dispositiu" + "Continua el restabliment" + "Es conservaran les dades del compte, contactes, preferències i les llistes de xats." + "Perdràs tots els missatges que estiguin desats únicament al servidor." + "Hauràs de tornar a verificar tots els teus dispositius i contactes existents." + "Només restableix la teva identitat si no tens accés a cap altre dispositiu on tinguis la sessió iniciada i has perdut la clau de recuperació." + "Si no pots confirmar la teva identitat, hauràs de restablir-la." + "Desactiva" + "Perdràs els missatges xifrats si tanques sessió en tots els dispositius." + "Segur que vols desactivar la còpia de seguretat?" + "Si elimines l\'emmagatzematge de claus, s\'eliminarà la teva identitat criptogràfica i les claus de missatge del servidor i es desactivaran les funcions de seguretat següents:" + "No tindràs l\'historial de missatges xifrats als dispositius nous" + "Perdràs l\'accés als teus missatges xifrats si tanques la sessió d\'%1$s a tot arreu" + "Estàs segur que vols desactivar l\'emmagatzematge de claus i eliminar-lo?" + "Obté una nova clau de recuperació si has perdut la que tens. Després de canviar la clau de recuperació, l\'antiga deixarà de funcionar." + "Genera una nova clau de recuperació" + "No ho comparteixis amb ningú!" + "Clau de recuperació canviada" + "Vols canviar la clau de recuperació?" + "Crea nova clau de recuperació" + "Assegura\'t que ningú vegi aquesta pantalla!" + "Torna a provar l\'accés a l\'emmagatzematge de claus." + "Clau de recuperació incorrecta" + "Si tens una clau de seguretat o una frase de seguretat, també funcionaran." + "Introdueix…" + "Has perdut la clau de recuperació?" + "Clau de recuperació confirmada" + "Introdueix clau de recuperació" + "Clau de recuperació copiada" + "Generant…" + "Desa clau de recuperació" + "Anota aquesta clau de recuperació en un lloc segur, com ara un gestor de contrasenyes, una nota xifrada o una caixa forta física." + "Toca per copiar la clau de recuperació" + "Desa la clau de recuperació en un lloc segur" + "Després d\'aquest pas, no podràs accedir a la nova clau de recuperació." + "Has desat la clau de recuperació?" + "L\'emmagatzematge de claus està protegit amb una clau de recuperació. Si necessites una nova clau de recuperació després d\'haver establerta, pots tornar-la a crear seleccionant ‘Canvia clau de recuperació’." + "Genera clau de recuperació" + "No ho comparteixis amb ningú!" + "Configuració de recuperació correcta" + "Configura la recuperació" + "Sí, restableix ara" + "Aquest procés és irreversible." + "Segur que vols restablir la teva identitat?" + "S\'ha produït un error desconegut. Comprova la contrasenya del compte i torna-ho a provar." + "Introdueix…" + "Confirma que vols restablir la teva identitat." + "Introdueix la contrasenya del compte per continuar" + diff --git a/features/securityandprivacy/impl/src/main/res/values-ca/translations.xml b/features/securityandprivacy/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..4d541af9d1 --- /dev/null +++ b/features/securityandprivacy/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,34 @@ + + + "És necessària una adreça perquè sigui visible al directori públic." + "Edita adreça" + "Afegeix adreça" + "Tothom ha de sol·licitar l\'accés." + "Sol·licita unir-t\'hi" + "Sí, activa el xifrat" + "Un cop activat, el xifrat d\'una sala no es pot desactivar. L\'històric de missatges només serà visible per als membres de la sala des que van ser convidats o des que s\'hi van unir. +Ningú a part dels membres de la sala podrà llegir els missatges. Això pot impedir que els bots i els ponts (\'bridges\') funcionin correctament. +No es recomana activar el xifrat a les sales que tothom pot trobar i unir-se." + "Vols activar el xifrat?" + "Un cop activat, el xifrat no es pot desactivar." + "Xifrat" + "Activa el xifrat d\'extrem a extrem" + "Tothom pot unir-s\'hi." + "Tothom" + "Només s\'hi poden unir les persones convidades." + "Només amb invitació" + "Accés" + "Actualment els espais no són compatibles" + "És necessària una adreça perquè sigui visible al directori públic." + "Adreça" + "Permet trobar aquesta sala cercant %1$s al directori públic de sales" + "Visible al directori públic" + "Tothom (historial públic)" + "Qui pot llegir l\'historial?" + "Membres, des de quan es van convidar" + "Membres (historial complet)" + "Les adreces de sala són maneres de trobar i accedir a les sales. Això també garanteix que puguis compartir fàcilment la teva sala amb altres persones. +Pots optar per publicar la teva sala al directori públic de sales del teu servidor local." + "Visibilitat" + "Seguretat i privadesa" + diff --git a/features/signedout/impl/src/main/res/values-ca/translations.xml b/features/signedout/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..6cba810df9 --- /dev/null +++ b/features/signedout/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,8 @@ + + + "Has canviat la contrasenya en una altra sessió" + "Has eliminat la sessió des d\'una altra sessió" + "L\'administrador del teu servidor t\'ha invalidat l\'accés" + "S\'ha tancat la sessió per algun dels motius enumerats a continuació. Torna a iniciar sessió per continuar utilitzant %s." + "Has tancat sessió" + diff --git a/features/space/impl/src/main/res/values-ca/translations.xml b/features/space/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..66ebf7edc4 --- /dev/null +++ b/features/space/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "Rols i permisos" + "Seguretat i privadesa" + diff --git a/features/space/impl/src/main/res/values-zh/translations.xml b/features/space/impl/src/main/res/values-zh/translations.xml index 784ab4bff4..cbe9d54c32 100644 --- a/features/space/impl/src/main/res/values-zh/translations.xml +++ b/features/space/impl/src/main/res/values-zh/translations.xml @@ -1,7 +1,7 @@ "选择所有者" - "%1$s (管理员)" + "%1$s(管理员)" "离开 %1$d 个房间和空间" diff --git a/features/startchat/impl/src/main/res/values-ca/translations.xml b/features/startchat/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..e249d61582 --- /dev/null +++ b/features/startchat/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,12 @@ + + + "Sala nova" + "Directori de sales" + "S\'ha produït un error en intentar iniciar un xat" + "Uneix-te a una sala mitjançant l\'adreça" + "Adreça invàlida" + "Introdueix…" + "S\'ha trobat una sala coincident" + "Sala no trobada" + "p. ex. #nom-sala:matrix.org" + diff --git a/features/userprofile/shared/src/main/res/values-ca/translations.xml b/features/userprofile/shared/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..bacb920a58 --- /dev/null +++ b/features/userprofile/shared/src/main/res/values-ca/translations.xml @@ -0,0 +1,19 @@ + + + "Bloqueja" + "Els usuaris bloquejats no podran enviar-te missatges i tots els seus missatges s\'amagaran. Pots desbloquejar-los en qualsevol moment." + "Bloqueja usuari" + "Desbloqueja" + "Podràs tornar a veure tots els seus missatges." + "Desbloqueja usuari" + "Bloqueja" + "Els usuaris bloquejats no podran enviar-te missatges i tots els seus missatges s\'amagaran. Pots desbloquejar-los en qualsevol moment." + "Bloqueja usuari" + "Perfil" + "Desbloqueja" + "Podràs tornar a veure tots els seus missatges." + "Desbloqueja usuari" + "Utilitza l\'aplicació web per verificar aquest usuari." + "Verifica %1$s" + "S\'ha produït un error en intentar iniciar un xat" + diff --git a/features/verifysession/impl/src/main/res/values-ca/translations.xml b/features/verifysession/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..d5ca8fb167 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,54 @@ + + + "No pots confirmar-la?" + "Crea nova clau de recuperació" + "Verifica aquest dispositiu per configurar missatges segurs." + "Confirma la teva identitat" + "Utilitza un altre dispositiu" + "Utilitza clau de recuperació" + "Ara pots llegir o enviar missatges de manera segura, i qualsevol persona amb qui xategis també confiarà en aquest dispositiu." + "Dispositiu verificat" + "Utilitza un altre dispositiu" + "Esperant un altre dispositiu…" + "Alguna cosa no ha anat bé. O bé s\'ha superat el temps màxim d\'espera de la sol·licitud o bé s\'ha denegat." + "Verifica que les emoticones següents coincideixen amb les que es mostren a l\'altre dispositiu." + "Compara emoticones" + "Verifica que les emoticones següents coincideixen amb les que es mostren al dispositiu de l\'altre usuari." + "Comprova que els números següents coincideixen amb els mostrats a l\'altra sessió." + "Compara números" + "Ara ja pots enviar i llegir missatges de manera segura al teu altre dispositiu." + "Ara pots confiar en la identitat d\'aquest usuari quan enviïs o rebis missatges." + "Dispositiu verificat" + "Introdueix clau de recuperació" + "S\'ha esgotat el temps d\'espera de la sol·licitud, s\'ha denegat la sol·licitud o ha fallat la verificació." + "Demostra que ets tu per poder accedir a l\'historial de missatges xifrats." + "Obre una sessió existent" + "Torna a intentar verificació" + "Estic a punt" + "Esperant a que coincideixin…" + "Compara un conjunt únic d\'emoticones." + "Compara les emoticones i assegura\'t que apareixen en el mateix ordre." + "Sessió iniciada" + "S\'ha esgotat el temps d\'espera de la sol·licitud, s\'ha denegat la sol·licitud o ha fallat la verificació." + "Error de verificació" + "Només continua si tu has iniciat aquesta verificació." + "Verifica l\'altre dispositiu per mantenir segur l\'historial de missatges." + "Ara ja pots enviar i llegir missatges de manera segura al teu altre dispositiu." + "Dispositiu verificat" + "Verificació sol·licitada" + "No coincideixen" + "Coincideixen" + "Assegura\'t que tens l\'aplicació oberta a l\'altre dispositiu abans d\'inicar la verificació des d\'aquí." + "Obre l\'aplicació en un altre dispositiu verificat" + "Per a més seguretat, verifica aquest usuari comparant un conjunt d\'emoticones vostres dispositius. Fes-ho a través d\'un mitjà de comunicació fiable, o en persona." + "Vols verificar aquest usuari?" + "Per a més seguretat, un altre usuari vol verificar la teva identitat. Se\'t mostrarà un conjunt d\'emoticones a comparar." + "Hauries de veure una finestra emergent a l\'altre dispositiu. Inicia, ara, la verificació des d\'allà." + "Inicia la verificació a l\'altre dispositiu" + "Inicia la verificació a l\'altre dispositiu" + "Esperant a l\'altre usuari" + "Un cop acceptat, podràs continuar amb la verificació." + "Per continuar, accepta la sol·licitud per iniciar el procés de verificació a l\'altra sessió." + "Esperant que s\'accepti la sol·licitud" + "S\'està tancant la sessió…" + diff --git a/libraries/androidutils/src/main/res/values-ca/translations.xml b/libraries/androidutils/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..aba099db1f --- /dev/null +++ b/libraries/androidutils/src/main/res/values-ca/translations.xml @@ -0,0 +1,4 @@ + + + "No s\'ha trobat cap aplicació compatible per gestionar aquesta acció." + diff --git a/libraries/dateformatter/impl/src/main/res/values-ca/translations.xml b/libraries/dateformatter/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..491793c728 --- /dev/null +++ b/libraries/dateformatter/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s a les %2$s" + "Aquest mes" + diff --git a/libraries/eventformatter/impl/src/main/res/values-ca/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..2caa14e8a3 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,73 @@ + + + "(la foto també ha canviat)" + "%1$s ha canviat la seva foto" + "Has canviat la teva foto" + "%1$s ha baixat a membre" + "%1$s ha baixat a moderador/a" + "%1$s han canviat el seu nom de visualització de %2$s a %3$s" + "Has canviat el teu nom de visualització de %1$s a %2$s" + "%1$s ha eliminat el seu nom de visualització (era %2$s)" + "Has eliminat el teu nom de visualització (era %1$s)" + "%1$s ha definit el seu nom de visualització a %2$s" + "Has definit el teu nom de visualització a %1$s" + "%1$s ha ascendit a administrador/a" + "%1$s ha ascendit a moderador/a" + "%1$s ha canviat la foto de la sala" + "Has canviat la foto de la sala" + "%1$s ha eliminat la foto de la sala" + "Has eliminat la foto de la sala" + "%1$s ha bandejat %2$s" + "Has bandejat %1$s" + "Has bandejat %1$s: %2$s" + "%1$s ha bandejat %2$s: %3$s" + "%1$s ha creat la sala" + "Has creat la sala" + "%1$s a convidat a %2$s" + "%1$s ha acceptat la invitació" + "Has acceptat la invitació" + "Has convidat a %1$s" + "%1$s t\'ha convidat" + "%1$s s\'ha unit a la sala" + "T\'has unit a la sala" + "%1$s ha sol·licitat unir-se" + "%1$s ha concedit l\'accés a %2$s" + "Has permès %1$s unir-se" + "Has sol·licitat unir-te" + "%1$s ha rebutjat la sol·licitud d\'unió a %2$s" + "Has rebutjat la sol·licitud d\'unió a %1$s" + "%1$s ha rebutjat la teva sol·licitud d\'unió" + "%1$s ja no es vol unir" + "Has cancel·lat la sol·licitud per unir-te" + "%1$s ha sortit de la sala" + "Has sortit de la sala" + "%1$s ha canviat el nom de la sala a: %2$s" + "Has canviat el nom de la sala a: %1$s" + "%1$s ha eliminat el nom de la sala" + "Has eliminat el nom de la sala" + "%1$s no ha fet canvis" + "No s\'han fet canvis" + "%1$s ha canviat els missatges fixats" + "Has canviat els missatges fixats" + "%1$s ha fixat un missatge" + "Has fixat un missatge" + "%1$s ha deixat de fixar un missatge" + "Has deixat de fixar un missatge" + "%1$s ha rebutjat la invitació" + "Has rebutjat la invitació" + "%1$s ha eliminat %2$s" + "Has eliminat %1$s" + "Has eliminat %1$s: %2$s" + "%1$s ha eliminat %2$s: %3$s" + "%1$s ha convidat a %2$s a la sala" + "Has convidat a %1$s a la sala" + "%1$s ha rebutjat la invitació a la sala de %2$s" + "Has rebutjat la invitació a la sala de %1$s" + "%1$s ha canviat el tema a: %2$s" + "Has canviat el tema a: %1$s" + "%1$s ha eliminat el tema de la sala" + "Has eliminat el tema de la sala" + "%1$s ha readmès %2$s" + "Has readmès %1$s" + "%1$s ha fet un canvi desconegut al seu tipus d\'usuari" + diff --git a/libraries/matrixui/src/main/res/values-ca/translations.xml b/libraries/matrixui/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..44d3e10c55 --- /dev/null +++ b/libraries/matrixui/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Envia invitació" + "Vols iniciar un xat amb %1$s?" + "Enviar invitació?" + "%1$s (%2$s) t\'ha convidat" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-ca/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..44bf749c0f --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,21 @@ + + + "L\'arxiu s\'eliminarà de la sala i el membres no en tindran accés." + "Eliminar el document?" + "Comprova la connexió a internet i torna-ho a provar." + "Els documents, àudios i missatges de veu que s\'hagin penjat a la sala es mostraran aquí." + "Encara no s\'han pujat documents" + "Carregant documents…" + "Carregant multimèdia…" + "Documents" + "Multimèdia" + "Les imatges i vídeos penjats a la sala es mostraran aquí." + "Encara no s\'ha pujat multimèdia" + "Multimèdia i documents" + "Format del document" + "Nom del document" + "No hi ha més fitxers a mostrar" + "No hi ha més multimèdia a mostrar" + "Pujat per" + "Pujat el" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-fi/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-fi/translations.xml index b3728cb9b7..05154b293c 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-fi/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-fi/translations.xml @@ -16,6 +16,7 @@ "Tiedostonimi" "Ei enää näytettäviä tiedostoja" "Ei enää näytettävää mediaa" + "Tiedoston tiedot" "Lähettäjä" "Lähetetty" diff --git a/libraries/mediaviewer/impl/src/main/res/values-ru/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ru/translations.xml index cdbe07f2e1..74b910faaa 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-ru/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-ru/translations.xml @@ -16,6 +16,7 @@ "Имя файла" "Больше нет файлов для отображения" "Больше нет медиа для отображения" + "Информация о файле" "Загружено" "Загружено" diff --git a/libraries/permissions/api/src/main/res/values-ca/translations.xml b/libraries/permissions/api/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..0b8527649b --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-ca/translations.xml @@ -0,0 +1,7 @@ + + + "Per permetre que l\'aplicació utilitzi la càmera, concedeix-li permís a la configuració del sistema." + "Concedeix el permís a la configuració del sistema." + "Per permetre que l\'aplicació utilitzi el micròfon, concedeix-li permís a la configuració del sistema." + "Per permetre que l\'aplicació mostri notificacions, concedeix-li permís a la configuració del sistema." + diff --git a/libraries/permissions/impl/src/main/res/values-ca/translations.xml b/libraries/permissions/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..206c416d8b --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,5 @@ + + + "Comprova que l\'aplicació pot mostrar notificacions." + "Comprova els permisos" + diff --git a/libraries/push/impl/src/main/res/values-ca/translations.xml b/libraries/push/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..56655d250d --- /dev/null +++ b/libraries/push/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,83 @@ + + + "Trucada" + "Escoltant esdeveniments" + "Notificacions sonores" + "Trucades amb so" + "Notificacions silencioses" + + "%1$s: %2$d missatge" + "%1$s: %2$d missatges" + + + "%d notificació" + "%d notificacions" + + "Tens missatges nous." + "📹 Trucada entrant" + "** No s\'ha pogut enviar. Obre la sala" + "Uneix-te" + "Rebutja" + + "%d invitació" + "%d invitacions" + + "T\'ha convidat a xatejar" + "%1$s t\'ha convidat a xatejar" + "T\'ha mencionat: %1$s" + "Missatges nous" + + "%d missatge nou" + "%d missatges nous" + + "Ha reaccionat amb %1$s" + "Marca com a llegit" + "Resposta ràpida" + "T\'ha convidat a unir-te a la sala" + "%1$s t\'ha convidat a unir-te a la sala" + "Jo" + "%1$s ha mencionat o respost" + "Estàs veient la notificació! Fes-hi clic!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + + "%d missatge notificat no llegit" + "%d missatges notificats no llegits" + + "%1$s i %2$s" + "%1$s en %2$s" + "%1$s en %2$s i %3$s" + + "%d sala" + "%d sales" + + "Sincronització en segon pla" + "Serveis de Google" + "No s\'ha trobat \'Serveis de Google Play\' vàlid. És possible que les notificacions no funcionin correctament." + "Usuaris bloquejats" + "Obté el nom del proveïdor actual." + "No s\'han seleccionat proveïdors push." + "Proveïdor actual de notificacions push: %1$s." + "Proveïdor actual de notificacions push" + "Assegura\'t que l\'aplicació admet com a mínim un proveïdor de notificacions push." + "No s\'ha trobat cap proveïdor de notificacions push." + + "S\'ha trobat %1$d proveïdor push: %2$s" + "S\'han trobat %1$d proveïdors push: %2$s" + + "L\'aplicació s\'ha creat per permetre: %1$s" + "Suport del proveïdor de notificacions Push" + "Comprova que l\'aplicació pot mostrar notificacions." + "No s\'ha fet clic a la notificació." + "No es pot mostrar la notificació." + "S\'ha fet clic a la notificació!" + "Mostrar notificació" + "Fes clic a la notificació per continuar el test." + "Assegureua\'t que l\'aplicació rep notificacions push." + "Error: el proveïdor push ha rebutjat la sol·licitud." + "Error: %1$s." + "Error, no s\'han pogut provar les notificacions push." + "Error, s\'ha acabat el temps d\'espera de la notificació push." + "El bucle de notificacions push ha tardat %1$d ms." + "Prova el bucle de notificacions push" + diff --git a/libraries/push/impl/src/main/res/values-zh/translations.xml b/libraries/push/impl/src/main/res/values-zh/translations.xml index 881dbcdc54..7532f16ba0 100644 --- a/libraries/push/impl/src/main/res/values-zh/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh/translations.xml @@ -65,7 +65,7 @@ "已屏蔽用户" "获取当前推送提供者的名称。" "未选择任何推送提供者。" - "当前推送提供这:%1$s 及当前分发器:%2$s . 但未找到分发器 %3$s。该 app 可能已被卸载?" + "当前推送提供者:%1$s 及当前分发器:%2$s。但未找到分发器 %3$s。该 app 可能已被卸载?" "当前推送提供者:%1$s ,但尚未配置分发器。" "当前推送提供者:%1$s。" "当前推送提供者:%1$s(%2$s)" diff --git a/libraries/pushproviders/firebase/src/main/res/values-ca/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..acd61bab06 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-ca/translations.xml @@ -0,0 +1,11 @@ + + + "Assegura\'t que Firebase estigui disponible." + "Firebase no està disponible." + "Firebase està disponible." + "Comprova Firebase" + "Assegura\'t que el token de Firebase està disponible." + "Token de Firebase desconegut." + "Token de Firebase: %1$s." + "Comprova el token de Firebase" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-ca/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..51096a1cfa --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-ca/translations.xml @@ -0,0 +1,10 @@ + + + "Assegura\'t que hi hagi distribuïdors d\'UnifiedPush disponibles." + "No s\'han trobat distribuïdors push." + + "%1$d distribuïdor trobat: %2$s." + "%1$d distribuïdors trobats: %2$s." + + "Prova UnifiedPush" + diff --git a/libraries/textcomposer/impl/src/main/res/values-ca/translations.xml b/libraries/textcomposer/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..f3b78b9dd2 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,31 @@ + + + "Afegeix fitxer adjunt" + "Activa/desactiva la llista amb punts" + "Cancel·la i tanca el format de text" + "Activa/desactiva bloc de codi" + "Afegeix llegenda" + "Missatge xifrat…" + "Missatge…" + "Missatge no xifrat…" + "Crea enllaç" + "Edita enllaç" + "%1$s, estat: %2$s" + "Aplica el format negreta" + "Aplica el format cursiva" + "desactivat" + "Aplica el format ratllat" + "Aplica el format subratllat" + "Activa/desactiva mode pantalla completa" + "Sagnia" + "Aplica format de codi" + "Estableix enllaç" + "Activa/desactiva llista numerada" + "Obre les opcions de redacció" + "Activa/desactiva cometes" + "Elimina enllaç" + "Treu sagnia" + "Enllaç" + "És possible que les llegendes no siguin visibles pels que utilitzin aplicacions antigues." + "Mantén premut per gravar." + diff --git a/libraries/troubleshoot/impl/src/main/res/values-ca/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..fe85dadb55 --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-ca/translations.xml @@ -0,0 +1,11 @@ + + + "Executa tests" + "Torna a executar tests" + "Alguns tests han fallat. Comprova\'n els detalls." + "Executa els tests per detectar qualsevol problema a la configuració que pugui fer que les notificacions no es comportin com s\'espera." + "Intenta solucionar" + "Tots els tests s\'han superat amb èxit." + "Resolució de problemes de notificacions" + "Alguns tests necessiten la teva atenció. Comprova\'n els detalls." + diff --git a/libraries/ui-strings/src/main/res/values-ca/translations.xml b/libraries/ui-strings/src/main/res/values-ca/translations.xml new file mode 100644 index 0000000000..d8abb7ff73 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-ca/translations.xml @@ -0,0 +1,387 @@ + + + "Elimina" + + "%1$d dígit introduït" + "%1$d dígits introduïts" + + "Amaga contrasenya" + "Uneix-te a la trucada" + "Vés al final" + "Només mencions" + "Silenciat" + "Noves mencions" + "Pàgina %1$d" + "Pausa" + "Missatge de veu, durada: %1$s, posició actual: %2$s" + "Camp de PIN" + "Reprodueix" + "Votació" + "Votació finalitzada" + "Reacciona amb %1$s" + "Reacciona amb altres emoticones" + "Llegit per %1$s i %2$s" + + "Llegit per %1$s i %2$d més" + "Llegit per %1$s i %2$d més" + + "Llegit per %1$s" + "Toca per mostrar-los tots" + "Elimina la reacció: %1$s" + "Envia fitxers" + "Mostra contrasenya" + "Inicia trucada" + "Menú d\'usuari" + "Mostra els detalls" + "Missatge de veu, durada: %1$s" + "Grava un missatge de veu." + "Atura gravació" + "Accepta" + "Afegeix llegenda" + "Afegeix a la cronologia" + "Tornar" + "Truca" + "Cancel·la" + "Cancel·la per ara" + "Tria una foto" + "Esborra" + "Tanca" + "Completa la verificació" + "Confirma" + "Confirma la contrasenya" + "Continua" + "Copia" + "Copia llegenda" + "Copia l\'enllaç" + "Copia l\'enllaç al missatge" + "Copia text" + "Crea" + "Crea sala" + "Desactiva" + "Desactiva el compte" + "Declina" + "Rebutja i bloqueja" + "Elimina votació" + "Desactiva" + "Descarta" + "Omet" + "Fet" + "Edita" + "Edita llegenda" + "Edita votació" + "Activa" + "Finalitza votació" + "Introdueix PIN" + "Has oblidat la contrasenya?" + "Reenvia" + "Enrere" + "Ignora" + "Convida" + "Convida persones" + "Convida gent a %1$s" + "Convida a la gent a %1$s" + "Invitacions" + "Uneix-te" + "Més informació" + "Surt" + "Surt del xat" + "Surt de la sala" + "Carrega més" + "Gestiona compte" + "Gestiona dispositius" + "Envia missatge" + "Següent" + "No" + "Ara no" + "D\'acord" + "Configuració" + "Obre amb" + "Fixa" + "Resposta ràpida" + "Cita" + "Reacciona" + "Rebutja" + "Elimina" + "Elimina llegenda" + "Elimina missatge" + "Respon" + "Respon al fil" + "Denuncia" + "Informa d\'un error" + "Denuncia contingut" + "Denuncia la conversa" + "Denuncia sala" + "Restableix" + "Restableix identitat" + "Torna-ho a provar" + "Torna a intentar desxifrar" + "Desa" + "Cerca" + "Envia" + "Envia missatge" + "Comparteix" + "Comparteix enllaç" + "Mostra" + "Torna a iniciar sessió" + "Tanca sessió" + "Tanca sessió igualment" + "Omet" + "Comença" + "Inicia un xat" + "Torna a començar" + "Inicia verificació" + "Toca per carregar el mapa" + "Fes una foto" + "Toca per veure opcions" + "Torna-ho a intentar" + "No fixis" + "Mostra a la cronologia" + "Mostra font" + "Sí" + "Sí, torna-ho a intentar" + "El servidor utilizat admet un nou protocol més ràpid. Tanca la sessió i torna-la a iniciar per actualitzar-lo. Si ho fas ara, evitaràs un tancament de sessió forçat quan s\'elimini l\'antic protocol (més endavant)." + "Actualització disponible" + "Sobre l\'aplicació" + "Política d\'ús a acceptar" + "Afegint llegenda" + "Configuració avançada" + "Analítiques" + "Has sortit de la sala" + "Aspecte" + "Àudio" + "Usuaris bloquejats" + "Bombolles" + "Trucada iniciada" + "Còpia de seguretat de xat" + "Copiat al porta-retalls" + "Drets d\'autor" + "Creant sala…" + "Sol·licitud cancel·lada" + "Ha sortit de la sala" + "Invitació rebutjada" + "Error de desxifrat" + "Opcions per a desenvolupadors" + "ID de dispositiu" + "Xat directe" + "No ho tornis a mostrar" + "No s\'ha pogut baixar" + "Baixant" + "(editat)" + "Editant" + "Editant llegenda" + "* %1$s %2$s" + "Fitxer buit" + "Xifrat" + "Xifrat activat" + "Introdueix PIN" + "Error" + "S\'ha produït un error i és possible que no rebis notificacions dels missatges nous. Pots resoldre els problemes de notificacions des de la configuració. + +Motiu: %1$s." + "Tothom" + "Ha fallat" + "Preferit" + "Afegit a preferits" + "Fitxer" + "Fitxer eliminat" + "Fitxer desat" + "Fitxer desat a Descàrregues" + "Reenvia missatge" + "Utilitzats freqüentment" + "GIF" + "Imatge" + "En resposta a %1$s" + "Instal·la APK" + "No s\'ha trobat l\'ID de Matrix, és possible que la invitació no s\'hagi rebut." + "Sortint de la sala" + "Línia copiada al porta-retalls" + "Enllaç copiat al porta-retalls" + "S\'està carregant…" + "Carregant més…" + + "%d més" + "%d més" + + + "%1$d membre" + "%1$d membres" + + "Missatge" + "Accions de missatge" + "Estil dels missatges" + "Missatge eliminat" + "Modern" + "Silencia" + "%1$s (%2$s)" + "Sense resultats" + "Sala sense nom" + "Sense xifrar" + "Fora de línia" + "Llicències de codi obert" + "o" + "Contrasenya" + "Persones" + "Enllaç permanent" + "Permís" + "Fixat" + "Comprova la connexió a Internet" + "Si us plau, espera…" + "Segur que vols finalitzar aquesta votació?" + "Votació: %1$s" + "Vots totals: %1$s" + "Els resultats es mostraran quan hagi finalitzat la votació" + + "%d vot" + "%d vots" + + "Política de privadesa" + "Sala privada" + "Sala pública" + "Reacció" + "Reaccions" + "Motiu" + "Clau de recuperació" + "Actualitzant…" + + "%1$d resposta" + "%1$d respostes" + + "Responent a %1$s" + "Informa d\'un problema" + "S\'ha enviat" + "Editor de text enriquit" + "Sala" + "Nom de sala" + "p. ex. el nom d\'un projecte o grup" + "Canvis desats" + "Desant" + "Bloqueig de pantalla" + "Cerca persones" + "Resultats de la cerca" + "Seguretat" + "Vist per" + "Envia a" + "S\'està enviant…" + "Ha fallat l\'enviament" + "Enviat" + "Servidor no compatible" + "URL del servidor" + "Configuració" + "Ubicació compartida" + "Tancant sessió" + "Alguna cosa ha anat malament" + "S\'ha produït un problema. Torna-ho a intentar." + "Iniciant xat…" + "Adhesiu" + "Correcte" + "Suggeriments" + "Sincronitzant" + "Text" + "Avisos de tercers" + "Fil" + "Tema" + "De què tracta aquesta sala?" + "No s\'ha pogut desxifrar" + "Enviat des d\'un dispositiu insegur" + "No tens accés al missatge" + "La identitat verificada del remitent s\'ha restablert" + "No s\'han pogut enviar invitacions a un o més usuaris." + "No s\'han pogut enviar les invitacions" + "Desbloqueja" + "No silenciïs" + "Trucada no compatible" + "Esdeveniment no compatible" + "Nom d\'usuari" + "Verificació cancel·lada" + "Verificació completada" + "Error de verificació" + "Verificat" + "Verifica dispositiu" + "Verifica identitat" + "Verifica usuari" + "Vídeo" + "Missatge de veu" + "Esperant…" + "Esperant missatge" + "Tu" + "La identitat de %1$s s\'ha restablert. %2$s" + "La identitat de %1$s %2$s s\'ha restablert. %3$s" + "(%1$s)" + "%1$s ha restablert la identitat." + "Omet la verificació" + "L\'enllaç %1$s et portarà a un altre lloc %2$s + +Segur que vols continuar?" + "Revisa aquest enllaç" + "Sala denunciada" + "S\'ha denunciat i abandonat la sala." + "Confirmació" + "Error" + "Correcte" + "Avís" + "Hi ha canvis sense desar." + "Els canvis no s\'han desat. Segur que vols tornar enrere?" + "Desar canvis?" + "Cerca emoticones" + "El servidor utilitzat s\'ha d\'actualitzar per admetre el servei d\'autenticació de Matrix i la creació de comptes." + "No s\'ha pogut crear l\'enllaç permanent" + "%1$s no ha pogut carregar el mapa. Torna-ho a provar més tard." + "No s\'han pogut carregar els missatges" + "%1$s no ha pogut accedir a la teva ubicació. Torna-ho a provar més tard." + "No s\'ha pogut pujar el missatge de veu." + "Missatge no trobat" + "%1$s no té permís per accedir a la teva ubicació. Pots activar l\'accés a Configuració." + "%1$s no té permís per accedir a la teva ubicació. Activa l\'accés a sota." + "%1$s no té permís per accedir al micròfon. Activa\'n l\'accés per poder gravar missatges de veu." + "Això pot ser degut a problemes de xarxa o del servidor." + "Aquesta adreça de sala ja existeix. Prova d\'editar el camp d\'adreça o de canviar el nom de sala" + "Alguns caràcters no estan permesos. Només s\'admeten lletres, dígits i els símbols següents: ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" + "Alguns missatges no s\'han enviat" + "S\'ha produït un error" + "🔐️ Uneix-te a %1$s" + "Ei, xateja amb mi a %1$s: %2$s" + "%1$s Android" + "Sacseja per informar d\'errors" + "No s\'ha pogut seleccionar el contingut. Torna-ho a provar." + "Prem un missatge i selecciona “%1$s“ per incloure\'l aquí." + "Fixa els missatges importants perquè es puguin trobar fàcilment" + + "%1$d missatge fixat" + "%1$d missatges fixats" + + "Missatges fixats" + "No pots confirmar-la? Ves al teu compte per restablir la identitat." + "Omet la verificació i envia" + "Pots ometre la verificació i enviar el missatge igualment o pots cancel·lar i tornar-ho a intentar més tard, quan s\'hagi tornat a verificar %1$s." + "El teu missatge no s\'ha enviat perquè la identitat verificada de %1$s s\'ha reiniciat" + "Envia el missatge igualment" + "%1$s està utilitzant un o més dispositius no verificats. Pots enviar el missatge igualment o pots cancel·lar-lo i tornar-ho a provar més tard quan %2$s hagi verificat tots els seus dispositius." + "El teu missatge no s\'ha enviat perquè %1$s no ha verificat tots els dispositius" + "Un o més dels teus dispositius no estan verificats. Pots enviar el missatge igualment o cancel·lar-lo i tornar-ho a intentar més tard després d\'haver verificat tots els dispositius." + "El missatge no s\'ha enviat perquè no has verificat un o més dels teus dispositius." + "No s\'ha pogut processar el contingut que s\'havia de pujar. Torna-ho a provar." + "No s\'han pogut obtenir els detalls d\'usuari" + "Missatge a %1$s" + "%1$s de %2$s" + "%1$s missatges fixats" + "Crregant missatge…" + "Veure-ho tot" + "Xat" + "Comparteix ubicació" + "Comparteix la meva ubicació" + "Obre a Apple Maps" + "Obre a Google Maps" + "Obre a OpenStreetMap" + "Comparteix aquesta ubicació" + "Missatge no enviat perquè %1$s ha restablert la seva identitat verificada." + "Missatge no enviat perquè %1$s no ha verificat tots els dispositius." + "Missatge no enviat perquè no has verificat un o més dels teus dispositius." + "Ubicació" + "Versió: %1$s (%2$s)" + "en" + "L\'històric de missatges no està disponible en aquest dispositiu" + "Has de verificar aquest dispositiu per accedir a l\'històric de missatges" + "No tens accés al missatge" + "No s\'ha pogut desxifrar el missatge" + "Aquest missatge s\'ha bloquejat perquè no has verificat el teu dispositiu o perquè el remitent a de verificar la teva identitat." + 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 eef46172bd..f1c7d8d225 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -9,6 +9,7 @@ "%1$d number sisestatud" "%1$d numbrit sisestatud" + "Kestus: %1$s" "Muuda tunnuspilti" "Täisaadress saab olema %1$s" "Krüptimise üksikasjad" @@ -33,6 +34,7 @@ "Taasesituse kiirus" "Küsitlus" "Lõppenud küsitlus" + "Asukoht: %1$s" "QR-kood" "Reageeri emotikoniga %1$s" "Reageeri mõne muu emotikoniga" @@ -48,12 +50,15 @@ "Jututoa tunnuspilt" "Saada faile" "Saatja asukoht" + "%1$s saatis selle %2$s" "Palun tee see ajapiiranguga toiming, sul on aega üks minut" "Seaded, vajalik on tegevus" "Näita salasõna" "Helista" "Alusta videokõnet" "Helista" + "Jutulõng „%1$s“ jututoas" + "Jutulõngad „%1$s“ jututoas" "Lõpetatuks märgitud jututuba" "Kasutaja tunnuspilt" "Kasutajamenüü" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index b3f0f32639..449fd0f352 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -9,13 +9,16 @@ "%1$d numero syötetty" "%1$d numeroa syötetty" + "Kesto: %1$s" "Muokkaa avataria" "Täysi osoite tulee olemaan %1$s" "Salauksen tiedot" "Laajenna viestin tekstikenttä" "Piilota salasana" + "Tiedot" "Liity puheluun" "Siirry loppuun" + "Siirry lukemattomiin" "Siirrä kartta sijaintiini" "Vain maininnat" "Mykistetty" @@ -32,6 +35,7 @@ "Toistonopeus" "Kysely" "Päättynyt kysely" + "Sijainti: %1$s" "QR-koodi" "Lisää reaktio: %1$s" "Reagoi muilla emojeilla" @@ -47,11 +51,15 @@ "Huoneen avatar" "Lähetä tiedostoja" "Lähettäjän sijainti" + "Lähettänyt %1$s aikaan %2$s" "Aikarajoitettu toimenpide vaaditaan, sinulla on yksi minuutti aikaa vahvistaa" + "Asetukset, toimenpide vaaditaan" "Näytä salasana" "Aloita puhelu" "Aloita videopuhelu" "Aloita äänipuhelu" + "Viestiketju huoneessa %1$s" + "Viestiketjut huoneessa %1$s" "Haudattu huone" "Käyttäjän avatar" "Käyttäjävalikko" @@ -69,6 +77,7 @@ "Soita" "Peruuta" "Peruuta toistaiseksi" + "Valitse tiedosto" "Valitse kuva" "Tyhjennä" "Sulje" @@ -88,12 +97,15 @@ "Deaktivoi tili" "Hylkää" "Hylkää ja estä" + "Poista" + "Poista tili" "Poista kysely" "Poista kaikki valinnat" "Poista käytöstä" "Hylkää" "Sulje" "Valmis" + "Lataa" "Muokkaa" "Muokkaa kuvatekstiä" "Muokkaa kyselyä" @@ -200,7 +212,9 @@ "Beeta" "Estetyt käyttäjät" "Kuplat" + "Puhelu hylätty" "Puhelu alkoi" + "Hylkäsit puhelun" "Keskustelujen varmuuskopiointi" "Kopioitu leikepöydälle" "Tekijänoikeudet" @@ -456,6 +470,9 @@ Haluatko varmasti jatkaa?" "Anteeksi, tapahtui virhe" "🔐️ Liity seuraani %1$s -sovelluksessa" "Hei, keskustele kanssani %1$s -sovelluksessa: %2$s" + "Reaaliaikaisen sijainnin jakaminen" + "Sijainnin jakaminen käynnissä" + "%1$s Reaaliaikainen sijainti" "%1$s Android" "Raivostunut ravistaminen ilmoittaa virheestä" "Näyttökuva" @@ -463,6 +480,10 @@ Haluatko varmasti jatkaa?" "Vaihtoehdot" "Poista %1$s" "Asetukset" + + "%1$d aste" + "%1$d astetta" + "Kukaan ei jaa sijaintiaan" "Jaetaan reaaliaikaista sijaintia" 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 545cf66c73..212b5eb79a 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -9,6 +9,7 @@ "%1$d chiffre saisi" "%1$d chiffres saisis" + "Durée: %1$s" "Modifier l’avatar" "L’adresse complète sera %1$s" "Détails du chiffrement" @@ -17,6 +18,7 @@ "Info" "Rejoindre l’appel" "Retourner à la fin de la conversation" + "Aller aux messages non lus" "Déplacer la carte vers ma position" "Mentions uniquement" "En sourdine" @@ -33,6 +35,7 @@ "Vitesse de lecture" "Sondage" "Sondage terminé" + "Position: %1$s" "Code QR" "Réagir avec %1$s" "Réagir avec d’autres émojis" @@ -48,12 +51,15 @@ "Avatar du salon" "Envoyer des fichiers" "Position de l’expéditeur" + "Envoyé par %1$s à %2$s" "Action limitée dans le temps requise, vous avez une minute pour effectuer la vérification" "Paramètres, action requise" "Afficher le mot de passe" "Démarrer un appel" "Passer un appel vidéo" "Lancer un appel vocal" + "Discussion dans %1$s" + "Discussions dans %1$s" "Salon clôturé" "Avatar de l’utilisateur" "Menu utilisateur" @@ -71,6 +77,7 @@ "Appel" "Annuler" "Annuler pour l’instant" + "Choisir un fichier" "Choisir une photo" "Effacer" "Fermer" @@ -463,6 +470,9 @@ Raison : %1$s." "Désolé, une erreur s’est produite" "🔐️ Rejoignez-moi sur %1$s" "Salut, parle-moi sur %1$s : %2$s" + "Partage de position en continu" + "Partage de position en cours" + "%1$s position en temps réel" "%1$s Android" "Rageshake pour signaler un problème" "Capture d’écran" @@ -470,6 +480,10 @@ Raison : %1$s." "Options" "Supprimer %1$s" "Paramètres" + + "%1$d degré" + "%1$d degrés" + "Personne ne partage sa position" "Partage de la position en direct" 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 7acfb90ef7..caca4b5014 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -9,6 +9,7 @@ "%1$d megadott számjegy" "%1$d megadott számjegy" + "Időtartam: %1$s" "Profilkép szerkesztése" "A teljes cím ez lesz: %1$s" "Titkosítás részletei" @@ -33,6 +34,7 @@ "Lejátszási sebesség" "Szavazás" "Befejezett szavazás" + "Pozíció: %1$s" "QR-kód" "Reagálás a következővel: %1$s" "Reagálás más emodzsikkal" @@ -48,12 +50,15 @@ "Szoba profilképe" "Fájlküldés" "Felhasználó tartózkodási helye" + "%1$s küldte ekkor: %2$s" "Időkorlátos művelet szükséges, egy perce van az ellenőrzésre" "Beállítások, beavatkozás szükséges" "Jelszó megjelenítése" "Hanghívás indítása" "Videohívás indítása" "Hanghívás indítása" + "Üzenetszál itt: %1$s" + "Üzenetszálak itt: %1$s" "Elévült szoba" "Felhasználói profilkép" "Felhasználói menü" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index c62c579f32..c32cbca475 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -8,6 +8,7 @@ "%1$d 桁入力済" + "長さ: %1$s" "アバターを編集" "完全なアドレスは %1$s になります。" "暗号化の詳細" @@ -16,6 +17,7 @@ "情報" "通話に参加" "一番下へ" + "未読に移動" "現在地に移動" "メンションのみ" "ミュート有効" @@ -32,6 +34,7 @@ "再生速度" "投票" "投票終了" + "位置: %1$s" "QRコード" "リアクション: %1$s" "他の絵文字でリアクション" @@ -46,12 +49,15 @@ "ルームのアバター" "ファイルを送信" "送信者の位置情報" + "%2$s に %1$s が送信" "1分以内に検証を完了してください" "処置が必要な設定" "パスワードを表示" "通話を開始" "ビデオ通話を開始" "音声通話を開始" + "%1$s のスレッド" + "%1$s のスレッド" "埋没したルーム" "ユーザーのアバター" "ユーザーメニュー" @@ -272,7 +278,7 @@ "メッセージアクション" "メッセージの送信に失敗" "メッセージのレイアウト" - "メッセージは削除されました" + "メッセージが削除されました" "モダン" "ミュート" "名前" @@ -455,6 +461,9 @@ "申し訳ありません。エラーが発生しました。" "🔐️ %1$s に参加してください" "%1$s で話しましょう: %2$s" + "ライブ位置情報共有" + "位置情報を共有しています" + "%1$s ライブ位置情報" "%1$s Android" "開発者にバグを報告するには端末を振ってください。" "スクリーンショット" @@ -462,6 +471,11 @@ "選択肢" "%1$s を削除" "設定" + "画像を左に回転" + + "%1$d°" + + "写真を編集" "誰も位置情報を共有していません" "ライブ位置情報を共有しています" 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 c654a66541..da3a5c2fea 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -10,6 +10,7 @@ "Wprowadzono %1$d cyfry" "Wprowadzono %1$d cyfr" + "Czas trwania: %1$s" "Edytuj awatar" "Podgląd pełnego adresu %1$s" "Szczegóły szyfrowania" @@ -18,6 +19,7 @@ "Informacje" "Dołącz do połączenia" "Przejdź na dół" + "Skocz do nieprzeczytanych" "Przesuń mapę do mojej lokalizacji" "Tylko wzmianki" "Wyciszone" @@ -34,6 +36,7 @@ "Prędkość odtwarzania" "Ankieta" "Zakończona ankieta" + "Pozycja: %1$s" "Kod QR" "Zareaguj z %1$s" "Zareaguj innym emoji" @@ -50,12 +53,15 @@ "Awatar pokoju" "Wyślij pliki" "Lokalizacja nadawcy" + "Wysłane przez %1$s o %2$s" "Wymagane jest działanie ograniczone czasowo, została jedna minuta" "Ustawienia, wymagane działanie" "Pokaż hasło" "Rozpocznij rozmowę" "Rozpocznij rozmowę wideo" "Rozpocznij połączenie głosowe" + "Wątek w %1$s" + "Wątki w %1$s" "Pokój nagrobkowy" "Awatar użytkownika" "Menu użytkownika" @@ -483,6 +489,13 @@ Czy na pewno chcesz kontynuować?" "Opcje" "Usuń %1$s" "Ustawienia" + "Obróć obraz w lewo" + + "%1$d stopień" + "%1$d stopnie" + "%1$d stopni" + + "Edytuj zdjęcie" "Nikt nie udostępnia swojej lokalizacji" "Udostępnianie lokalizacji na żywo" 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 26e1dcfe8d..3596bc04fe 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -15,6 +15,7 @@ "Сведения о шифровании" "Развернуть поле ввода" "Скрыть пароль" + "Информация" "Присоединиться к звонку" "Перейти вниз" "Переместить карту к моему местоположению" @@ -50,6 +51,7 @@ "Отправить файлы" "Местоположение отправителя" "Требуется действие, на которое есть ограничение по времени, у вас есть одна минута для проверки" + "Настройки, требуется действие" "Показать пароль" "Начать звонок" "Начать видеозвонок" @@ -71,6 +73,7 @@ "Позвонить" "Отмена" "Пока отменить" + "Выберите файл" "Выбрать фото" "Очистить" "Закрыть" @@ -90,12 +93,15 @@ "Отключить учётную запись" "Отклонить" "Отклонить и заблокировать" + "Удалить" + "Удалить аккаунт" "Удалить опрос" "Отменить выбор" "Отключить" "Отменить" "Закрыть" "Готово" + "Скачать" "Редактировать" "Изменить подпись" "Редактировать опрос" @@ -202,7 +208,9 @@ "Бета-версия" "Заблокированные пользователи" "Пузыри" + "Вызов отклонен" "Звонок начат" + "Ты отклонил звонок" "Резервная копия чатов" "Скопировано в буфер обмена" "Авторское право" @@ -465,6 +473,9 @@ "Произошла ошибка" "🔐️ Присоединяйтесь ко мне в %1$s" "Привет, давай поболтаем в %1$s: %2$s" + "Отправка местонахождения в реальном времени" + "Определение местоположения в процессе" + "Текущее местоположение %1$s" "%1$s Android" "Встряхните устройство, чтобы сообщить об ошибке" "Скриншот" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 9cea16d2f0..1b50794182 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -8,6 +8,7 @@ "已输入 %1$d 个数字" + "持续时间:%1$s" "编辑头像" "完整地址为 %1$s" "加密详情" @@ -16,6 +17,7 @@ "信息" "加入通话" "跳转到底部" + "跳转到未读" "将地图移动到我的位置" "仅提及" "通知已关闭" @@ -32,6 +34,7 @@ "播放速度" "投票" "投票已结束" + "位置:%1$s" "二维码" "使用 %1$s 反应" "使用其它 Emoji 做出反应" @@ -46,11 +49,14 @@ "房间头像" "发送文件" "发送方位置" + "由 %1$s 发送于 %2$s" "请求的操作有时间限制,你有 1 分钟的时间来验证" "显示密码" "开始通话" "开始视频通话" "开始语音通话" + "位于 %1$s 中的消息列" + "位于 %1$s 中的消息列" "已封存的房间" "用户头像" "用户菜单" @@ -192,7 +198,7 @@ "可接受的使用政策" "添加账户" "添加账户" - "添加标题" + "正在添加标题" "高级设置" "一张图片" "分析" @@ -211,7 +217,7 @@ "版权" "正在创建房间…" "正在创建空间…" - "请求已取消" + "申请已取消" "离开房间" "离开空间" "邀请已拒绝" @@ -225,7 +231,7 @@ "正在下载" "(已编辑)" "正在编辑" - "编辑标题" + "正在编辑标题" "* %1$s %2$s" "空文件" "加密" @@ -407,10 +413,10 @@ "由于你当时不在房间内,%1$s 已将消息向你共享。" "此房间已配置为允许新成员阅读历史。%1$s" "%1$s 的数字身份已重置。%2$s" - "%1$s %2$s 的数字身份已重置。%3$s" + "%1$s(%2$s)的数字身份已重置。%3$s" "(%1$s)" "%1$s 的数字身份已重置。" - "%1$s %2$s 的数字身份已重置。%3$s" + "%1$s(%2$s)的数字身份已重置。%3$s" "撤回验证" "允许访问" "链接 %1$s 将跳转至外部网站 %2$s @@ -422,7 +428,7 @@ "允许的最大文件大小为:%1$s" "文件太大,无法上传" "已举报房间" - "举报并离开房间" + "已举报并离开房间" "确认" "错误" "成功" @@ -463,6 +469,11 @@ "选项" "移除 %1$s" "设置" + "向左旋转图像" + + "%1$d 度" + + "编辑照片" "目前无人分享其位置" "共享实时位置" @@ -517,7 +528,7 @@ "消息未能发送,因为 %1$s 尚未验证所有设备。" "消息未能发送,因为你有尚未验证的设备。" "位置" - "版本:%1$s (%2$s)" + "版本:%1$s(%2$s)" "zh-Hans" "历史消息在此设备上不可用" "你需要验证此设备才能访问历史消息" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index e5bc31396f..0b9490ae59 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -18,6 +18,7 @@ "Info" "Join call" "Jump to bottom" + "Jump to unread" "Move the map to my location" "Mentions only" "Muted" @@ -480,6 +481,12 @@ Are you sure you want to continue?" "Options" "Remove %1$s" "Settings" + "Rotate the image to the left" + + "%1$d degree" + "%1$d degrees" + + "Edit photo" "Nobody is sharing their location" "Sharing live location" diff --git a/plugins/src/main/kotlin/extension/locales.kt b/plugins/src/main/kotlin/extension/locales.kt index 650fbc4d72..2774b078b5 100644 --- a/plugins/src/main/kotlin/extension/locales.kt +++ b/plugins/src/main/kotlin/extension/locales.kt @@ -5,6 +5,7 @@ package extension val locales = setOf( "be", "bg", + "ca", "cs", "cy", "da", diff --git a/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png new file mode 100644 index 0000000000..f79c47c87a --- /dev/null +++ b/screenshots/de/features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8dcfd0a6965522ddc3d1a0571176eb6889bbf15a390ea7d4348f002ec9696d5 +size 33189 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index c39909cf49..9dfcbcf1f6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10355aa4b00cbaa5ccded1d03d23db71907f9943ebd40dfec50545c046991b61 -size 49461 +oid sha256:ce3c8e771e43c186cadcf495a7d85d6ad36c089373782c209170282e0e5fb1fb +size 49709 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index 7e18f43981..26411218a7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a2132a405f40d577a61420b4ad2d7bb9f55f67b5fe1b07b4ddb0daff10b8b89 -size 47704 +oid sha256:db7a53d858a08f1396a9c04b6a253623fa25160f90de62be9dc187f72fccedbd +size 48374 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index 4cd5290a7c..db935d5dc9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3003b423f3fa7504581ecf60cfbb82452eb17267bbc44f3b9439642da0ff096 -size 46416 +oid sha256:38e49c07c2f49bee8ba4d37f684be34ccb247bfecbfea5b1733e76246d160a45 +size 47086 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index 0086307ec6..d0343f2f12 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bca6f953108331e512e3b0bca6086be207c1b1d2835d1dc220673098f5e3a9d -size 48263 +oid sha256:1f91bfd31c40b1cea5b4dd763dbc92eaaf7335d078ac496b51b05617c01f8b96 +size 48934 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index 0a99e31a52..12ccf2ffc3 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:494b1135a849734bce082b30aaeb491aebdb95cd67e6150be7334b55fe9fc0eb -size 48159 +oid sha256:d85a9aa1456dccaef8a7cb2c8b8c756e54c4fe03402d39d1240624d279449c28 +size 48821 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png index 110a14531d..625ef06b29 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e147c903449a6c5ba6fa5041f73217c29965c2b4c2a165a96d36aebd2e2691e -size 49059 +oid sha256:1b65c937240f2a5bee4e4170a511fff5437c5bce47fb987674548487fcf72fd1 +size 49350 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png index 469a77a49a..0ab84d817e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5590f66df4e26885e4b4776de840a9b1e4877a579f34769fe652172d01f5e374 -size 49554 +oid sha256:2f965ec5c45372e04fdcdc1c3c65633551f83e8dede75df8e8c329f849f487bf +size 49885 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png index 7235a7a8e9..4798c4bc07 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbcc418cc648fe137d32a0455af4c0235c5c9269c49826c28dcdb22e7997638e -size 48441 +oid sha256:b68d23accbe81e6913d16ef6bb15712105422911905f274d66ef656f98d8a28f +size 49201 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png index 6389db0432..11a68a7982 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8d7ee825e1e2cb28babc26bdd2de3747c99bff0b95f6c7e4d8a2b2dd9f9dd49 -size 47758 +oid sha256:3699ee438dbbd4aa0e5c5cd5a054304888421cb8c8a822dc52cb8369816f7feb +size 48429 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png index 89c1174fd0..de0323860c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e6f13ca775952d7a06d82c41519a5f480f156eabd015a2020e0a5cf9d435765 -size 44694 +oid sha256:d3eada7d271b1f649d931e5077fc04e232046fd4171881c673bab8eaf990f92b +size 44452 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png index 456f8db66c..65d67f1746 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c84a92f1c008dffb95cf4aa1337a35ba208d3702decf912e084937665079d947 -size 44674 +oid sha256:934be11ba08bc83461c1d859b3cf8b6f367feb89f6a779689ece209c30d8eaa8 +size 44441 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index 2f9e2bc268..637fe31e2f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29f45a3272a173614152dd6e7b0d3dc08de6d3b1f86c9683b267e97db773fb19 -size 43163 +oid sha256:540a461ec0a84d22269abc644dd62444223818e337edbab2f79317f80cc24696 +size 44107 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png index fb90141dde..bfa129700d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc70b1f6c6152f0f57b4f8586d781938c12af8bc61b517a232b779739eea6c72 -size 48521 +oid sha256:30918ebc116ebf2058aa619debfa9165d5ef95feb916a83e94e49d09377435e8 +size 49193 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png index 10f31671ce..8a5276b209 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e5d32aea59c328b24d0a56bede3110e5b4a1d90a9b5eeb9bffffd30de5a41b6 -size 48437 +oid sha256:5cf3437703035eec0dd9623032bb62f1472b1a5fe996b7b85b9050385eb118ba +size 49109 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png index 2a3f4a388a..a9e5ff3132 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddddf06d51977bcd997e3680d2fe3ff4f9d8102848980cf88b4d2ef08e37d5f9 -size 48004 +oid sha256:983d019c59d97d4bbe5b42562d6d028bb094a5b405fb2adc033232838952890d +size 48673 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index 28f7d63b1a..404ba5c5c1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5ffb5c4bb36e659d6650acfc43c560d6c6cfc26f7b663e6de036c8d210c3c53 -size 42627 +oid sha256:1c79eb29314fbb2378034b03d368d5ca49ae49c6a53387784445a0b97e3cc56e +size 43324 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index b945a5f082..b492032c83 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd4f6d16dbb22cd3968e4fb3a1408ee6387bb3f7d81388b18e4b4d7cdc24a2b8 -size 45416 +oid sha256:d605c8379ff6844f7581231574cdfb50e5bce0b2e33c886fd43b520576f9149a +size 45963 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index 291e381783..09c0f584a7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19e656cf990a24bb08da6b7205a5fc1d39c76bbec967781cf0ea2a55bf7d11ab -size 46989 +oid sha256:355905f6a0fc25abe31db1359f24af1acb60bb3a013e697663c3ee9f87a9be52 +size 47386 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index 8ad5518a4c..0fd7d3e667 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df3eb3ec43a8219be6e5fb27ae316222bf049d73d897712e9e4981c7cd482f66 -size 44290 +oid sha256:a378c9b3a9bde80050444029ec6eaaaf76a5182c45babc898dbea24207466a32 +size 44054 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index 2524ad82a5..6392ad39ea 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:babe700b98176da34b30db8fcd367f13b26eb0b8216f8a486d0462dccdbe3068 -size 44782 +oid sha256:737de18454bbb78bd9ca789ba1ab821f203c54d55301a5851ae01e336cc42260 +size 45324 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index d3fe3eeec7..425e08b224 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e78510f867c14a8cc9e98d6bf36880136f36cdf08cf8940b9a5f54e327a1f115 -size 49064 +oid sha256:e2ce5bb8422a493541b30fe3eb95f756367358e039b759967e1f4d96f7cf337a +size 49729 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index ae3e083f08..6be8a7cc91 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:627f61625aadbd1801d38dec33a107c72d9c2bc10057ab85ed15207557fb8343 -size 48379 +oid sha256:a4fd1c7495908336646a05655f1c4c2d2ea0ece1e690f69164cc2946273a077c +size 49038 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index e52d7bbc87..8927152505 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3da232ee647be1a9722a00d4884f6f559cddc83f642aa07c3cbc4fb35746028 -size 47649 +oid sha256:12d25ae598f1a05aa86f0125b6acbaa4137becc2900a2ac649880c521ba2ef52 +size 48034 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index a825b90d21..86a072cb2b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b17db5e3de5f2a7cd8ab5adb998b9343993aeb4ca7e072287e0f60064638155 -size 50517 +oid sha256:7382605cb34650919a0b9f1d1e9460e28ce963dc4ff899fc6906dfcdd73dda50 +size 50783 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index 1e506d9628..b3d5dd204c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9165fa335486aa0463e9ee43b9e7c581a1c9bcd81f605a193d217fac58bff01 -size 48642 +oid sha256:c6f057a687620b49fde08e0c241aadca011d87154e73b7cbaf21a22896de07c9 +size 49348 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index 15456d858a..abf7a28899 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66d934f6d2fb764fd8d856e19671a47eb96bf79d1904222f12ba2c04ff815472 -size 47449 +oid sha256:a08b46085a213f7c6a93f24c2afc04cab4a0c30369d6eaf552cc6e58f50aea34 +size 48160 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index 759eda79f1..257169012b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6dfedba8b70af6389b88143e0c1c59dc9dbacba06b985fb791f9d5ef6b76d65 -size 49222 +oid sha256:e624adcc59354fc21c960547d0766929b95e1a2745b667eb57dd5e62d3ca8451 +size 49914 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index b50b33e08a..3804c84919 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:114059cd1d74d7e67039fdab4f4a37306badb38b2811b7cb494fd47a3de7dd52 -size 49118 +oid sha256:60cc99a113658fc4222b68b4801066c112d02dbd5c5b39e12471f77bb71ab348 +size 49807 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png index 09a418c30b..080e2d2199 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:319b86e201bbcf3efb4ca2cceef15e7bba83800202c110439b5bab58d94aaa9d -size 50069 +oid sha256:fd16c5e9846823589ad5b22568c25b0cf3335cba8b467ceb3c9186e5b84e7fc7 +size 50318 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png index 9bb6b27c92..f80834a2e0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ddeb573fd6c83a7b0d9e00947a1f1b87c27eda69796285c7b9a6cee487ccbb1 -size 50653 +oid sha256:6d3dbdcfe1dedaabb5d03dbdcc2a2f377fb6d1aafccd6b70cb147a97bf23b1c5 +size 50929 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png index 8c769eb3c2..b43767563e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a092056f5ae3f2bf40db0e1727afd191bf19b2bffea9bbeaa9cd5822b2235af -size 49426 +oid sha256:0761670005dd4463b662efb563a5270f89790470017d42001d00ce44e96f045c +size 50201 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png index b7e8d5e7da..3c0a33e5fc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f300dbb1b0868fa619edef30e43231b5c2cb777bee2af56a8f3dfe42253db318 -size 48999 +oid sha256:6361321527d1bfa1ea84b361dec64c3746a85079a4048666e4016f52d3d34246 +size 49681 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png index 602a4de377..c3b0b28c5d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b7f04ee27a8cdc0b9cde949d6167f013cbf2cbfcf591a54262ca697fd54ab57 -size 46011 +oid sha256:f5bbd36791567c89996dcd755b3ff4f956f69f1ff3f6f66595f16e1e733fe0a7 +size 45751 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png index 0bb73f7526..7dc11fb06d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d39a3067e1df646444276e13c1709e5c0ab0b27bb313531bdedde43fa056d6c -size 45947 +oid sha256:3bf0969b8a24c8d06fb3253646d6e2d0a94572cc346a8a9f9522ae48e5b4217a +size 45686 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index 286ac63579..4fb1d5bebc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62ec3e5198b9d783cc7dcff94c39dec2ddd7edabbcbf1e272418e5446ec6d854 -size 44182 +oid sha256:3cfbd204c871feea36d0ac5ec740049488a9d3511156cf7a3e30d724d62fbdae +size 45187 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png index 673cdd0c30..96b2a06800 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60c1951da07af010cd27d8622dcef6f953d369df9143a6c09e479bd26ea3aea9 -size 49487 +oid sha256:84af752733042d4d487a934144525bbcf31cf9c1cb1454a5561ea9b3bf794afd +size 50205 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png index 49483a49d7..3576b6b30c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:951b4ed6186db857355229f9518aab6e72d0e6089e750eb58f1d21c23af54a58 -size 49415 +oid sha256:2a5c142c8539e0a7d40f4455e96d3d5fe1003f2cef84095709f15fd63b945a06 +size 50130 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png index e49a2edf1b..6905512be4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9036a65f211de57afb08e2a8a1edc679690286063855146616d1a9205f5e92ba -size 48963 +oid sha256:375274f827cb0b1a56461a95ebbbc12ab8dead23a41059b0909d87892b5b7f74 +size 49645 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index 037c1a51b9..23620c662b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c42fc0297235872ed4a405cbf73dbe68646b4ba2f2d8b4ab2a2c759372aedff5 -size 43575 +oid sha256:9737debc2f342c5ce2b20f48151597e164526297d2789b04bfda74e4cc70153c +size 44357 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index c7dd2f7b1a..aca9ef2193 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36df2a8757b8fea88e4d03150b7c9f1349e4112b1c7172224818cf394eb8755f -size 46360 +oid sha256:3d16e1a72ac9f47e0236b5ce3bf82c7e8d0d3ca0b245aba5fc751a0b9bed6cae +size 46946 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index 731d5599be..ab1ea77140 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73a5e4314e7d2cfffff54c1181aab1fb95227172758299c06754e97b59a58bba -size 47972 +oid sha256:e483482b88e7edc6724459da394f87a587ada9ddefea5d05d9bdcfe57e34ec47 +size 48423 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index fb6a42fde4..bd270b4ed0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9dec4b40543155b80a9d1db51a97f269d6188c0ea84384abf37e7c32a953a4c3 -size 45565 +oid sha256:7c8130c023137a1c3872202df289f364a3cc85217c7b0d7d36609d1fe896e96c +size 45304 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index 6ce7d985b9..26e23487c0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d575f4e716de7fc5e1d376a8225f69bcf5375ea8a1555baa8d27d0481b45d4b -size 46038 +oid sha256:6c67de39146bb8c0049de90223d4a7293e3f42ab8f3780cc0980e62c97d10760 +size 46651 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index 28cb1f210b..2001c831b0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a4d42087d391ecc107d9f485e97499a9952f85c7555ac4428d36f4565c75dba -size 50112 +oid sha256:69695044a9afdc82d1a65e0911effcbb660dd550c31d5b8ae1f6a0f674dacd30 +size 50843 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index 8c515e1bfe..bbec3a797e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5b97717f51c11b13b760cdb6922587db19d962f1f47c7c45bb39de2fc7880af -size 49374 +oid sha256:acbbccc700060f3511bdc2a44661ecd1442bb8db107c52d8a3ea93085e3cc370 +size 50181 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index 04877ee746..f1098ffed9 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29a2b72b3ab467665c819763eee041fe349d26d9d2b6b724677a071dfcb84450 -size 48626 +oid sha256:891f50447359e8697ca26ac27179fb1687851c24156d36006764c874592c7584 +size 49072 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 08d02138ef..119bc1e013 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -2,99 +2,99 @@ export const screenshots = [ ["en","en-dark","de",], ["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",0,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20588,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20595,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20588,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20588,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20588,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20588,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20588,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20588,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20588,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20588,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20588,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20588,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20588,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20595,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20595,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20595,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20595,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20595,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20595,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20595,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20595,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20595,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20595,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20595,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20588,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20588,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20595,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20595,], ["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",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20595,], ["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",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20588,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20588,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20588,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20588,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20588,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20588,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20588,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20588,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20588,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20588,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20595,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20595,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20595,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20595,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20595,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20595,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20595,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20595,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20595,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20588,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20588,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20588,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20588,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20588,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20595,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20595,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20595,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20595,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20595,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20588,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20588,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20588,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20588,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20595,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20595,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20595,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20595,], ["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",20588,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20595,], ["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",20588,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20595,], ["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",20588,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20595,], ["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",20588,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20595,], ["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,], @@ -104,19 +104,19 @@ 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_AttachmentsPreviewView_0_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20588,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20588,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20595,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20588,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20595,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -146,22 +146,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20588,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20595,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20588,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20588,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20595,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20588,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20588,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20588,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20588,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20588,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20595,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20595,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20595,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20595,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20595,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -172,141 +172,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20588,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20588,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20595,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20595,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20588,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20595,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20588,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20588,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20588,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20588,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20588,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20588,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20595,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20595,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20595,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20595,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20595,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20595,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20588,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20588,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20588,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20595,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20595,], ["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",20588,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20588,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20588,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20588,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20588,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20595,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20595,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20595,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20595,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20595,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20588,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20595,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20588,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20588,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20588,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20588,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20588,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20588,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20588,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20588,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20595,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20595,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20595,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20595,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20595,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20595,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20595,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20595,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20588,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20595,], ["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",20588,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20588,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20588,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20588,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20588,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20588,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20588,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20595,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20595,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20588,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20588,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20588,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20588,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20588,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20588,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20595,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20595,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20595,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20595,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20595,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20588,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20588,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20588,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20588,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20588,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20588,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20588,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20588,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20588,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20588,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20588,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20588,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20588,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20588,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20588,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20588,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20588,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20588,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20588,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20588,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20595,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20595,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20595,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20595,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20595,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20595,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20595,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20595,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20595,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20595,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20595,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20595,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20595,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20595,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20595,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20595,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20595,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20595,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20595,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20595,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20588,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20588,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20588,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20588,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20588,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20588,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20588,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20595,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20595,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20595,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20595,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20595,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20595,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20595,], ["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",20588,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20588,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20588,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20595,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20595,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20595,], ["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",20588,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20595,], ["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",20588,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20588,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20588,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20588,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20588,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20588,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20588,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20588,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20588,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20595,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20595,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20595,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20595,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20595,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20595,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20595,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20595,], ["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,], @@ -319,20 +319,20 @@ 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",20588,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20588,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20588,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20588,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20588,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20588,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20588,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20588,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20588,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20588,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20588,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20588,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20588,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20588,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20595,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20595,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20595,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20595,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20595,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20595,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20595,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20595,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20595,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20595,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20595,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20595,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20595,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20595,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -340,29 +340,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20588,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20588,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20595,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20595,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20588,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20588,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20588,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20588,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20588,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20588,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20588,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20588,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20588,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20588,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20595,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20595,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20595,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20595,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20595,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -382,49 +382,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20588,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20588,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20588,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20595,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20595,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20595,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20588,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20588,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20595,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20595,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20588,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20595,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20588,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20588,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20588,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20588,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20588,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20595,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20595,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20595,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20595,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20595,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20588,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20588,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20595,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20595,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20588,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20588,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20595,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20595,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20588,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20588,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20588,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20588,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20588,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20588,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20588,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20588,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20588,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20588,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20588,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20588,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20588,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20595,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20595,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20595,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20595,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20595,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20595,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20595,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20595,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20595,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20595,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20595,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20595,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20595,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -439,8 +439,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_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",20588,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20588,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20595,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20595,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -448,119 +448,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20588,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20595,], ["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",20588,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20595,], ["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",20588,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20588,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20595,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20595,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20588,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20588,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20595,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20588,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20588,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20588,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20595,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20595,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20588,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20588,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20588,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20595,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20588,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20588,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20588,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20595,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20595,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20595,], ["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",20588,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20588,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20588,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20588,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20588,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20595,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20595,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20595,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20588,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20588,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20595,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20595,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20588,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20588,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20588,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20595,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20595,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20588,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20588,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20588,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20588,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20588,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20588,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20595,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20588,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20595,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -613,48 +613,48 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], -["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20591,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20595,], ["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",20588,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20595,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20588,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20588,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20588,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20595,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20595,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20595,], ["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",20588,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20588,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20588,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20588,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20588,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20588,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20588,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20588,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20588,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20588,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20588,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20588,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20588,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20588,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20588,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20588,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20588,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20588,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20588,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20588,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20588,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20588,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20588,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20588,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20588,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20588,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20588,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20595,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20595,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20595,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20595,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20595,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20595,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20595,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20595,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20595,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20595,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20595,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20595,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20595,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20595,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20595,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20595,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20595,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20595,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20595,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20595,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20595,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20595,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20595,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20595,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20595,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20595,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20595,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20588,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20588,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20588,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20588,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20595,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20595,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20595,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20595,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -667,26 +667,26 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20588,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20588,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20588,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20588,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20588,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20588,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20595,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20595,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20595,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20595,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20595,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20595,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20588,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20588,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20595,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -694,10 +694,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20588,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], @@ -707,7 +707,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], @@ -717,10 +717,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20588,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], @@ -730,7 +730,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20588,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20595,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -744,7 +744,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",20588,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20595,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -753,7 +753,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20588,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20595,], ["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,], @@ -762,24 +762,24 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20588,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20588,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20588,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20588,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20588,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20588,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20591,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20588,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20588,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20588,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20588,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20588,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20588,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20588,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20588,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20588,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20595,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20595,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20595,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20595,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20595,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20595,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20595,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20595,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20595,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20595,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20595,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20595,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20595,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20595,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20595,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20595,], ["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",20588,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20595,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -790,117 +790,117 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20588,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20588,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20588,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20595,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20595,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20595,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20588,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20588,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20595,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20588,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20595,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20588,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20588,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20595,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20588,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20588,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20588,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20588,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20588,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20588,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20595,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20595,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20595,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20595,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20595,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20595,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20588,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20588,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20595,], ["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",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20588,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20588,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20588,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20588,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20588,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20588,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20595,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20595,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20595,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20595,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20595,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20588,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20588,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20588,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20588,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20588,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20595,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20595,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20595,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20595,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20595,], ["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",20588,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20588,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20588,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20588,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20588,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20588,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20588,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20588,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20588,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20588,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20588,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20595,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20595,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20595,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20595,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20595,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20595,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20595,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20595,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20595,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20595,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20595,], ["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,], @@ -914,225 +914,225 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20588,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20588,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20595,], ["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","",20588,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20588,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20595,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20595,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20588,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20588,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20588,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20588,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20588,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20588,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20588,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20588,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20588,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20588,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20588,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20588,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20588,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20588,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20588,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20588,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20595,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20595,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20595,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20595,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20595,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20595,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20595,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20595,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20595,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20595,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20595,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20595,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20595,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20595,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20595,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20595,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20588,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20588,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20595,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20595,], ["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",20588,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20588,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20588,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20588,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20588,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20588,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20588,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20595,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20595,], ["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",20588,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20588,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20588,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20588,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20588,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20588,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20588,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20588,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20588,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20588,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20588,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20588,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20588,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20588,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20588,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20588,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20588,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20595,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20595,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20595,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20595,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20595,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20595,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20595,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20595,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20595,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20595,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20595,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20595,], ["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",20588,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20588,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20588,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20588,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20588,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20595,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20595,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20595,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20595,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20595,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20588,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20588,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20595,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20595,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20588,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20588,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20588,], -["features.roomdetails.impl_RoomDetails_0_en","",20588,], -["features.roomdetails.impl_RoomDetails_10_en","",20588,], -["features.roomdetails.impl_RoomDetails_11_en","",20588,], -["features.roomdetails.impl_RoomDetails_12_en","",20588,], -["features.roomdetails.impl_RoomDetails_13_en","",20588,], -["features.roomdetails.impl_RoomDetails_14_en","",20588,], -["features.roomdetails.impl_RoomDetails_15_en","",20588,], -["features.roomdetails.impl_RoomDetails_16_en","",20588,], -["features.roomdetails.impl_RoomDetails_17_en","",20588,], -["features.roomdetails.impl_RoomDetails_18_en","",20588,], -["features.roomdetails.impl_RoomDetails_19_en","",20588,], -["features.roomdetails.impl_RoomDetails_1_en","",20588,], -["features.roomdetails.impl_RoomDetails_20_en","",20588,], -["features.roomdetails.impl_RoomDetails_21_en","",20588,], -["features.roomdetails.impl_RoomDetails_22_en","",20588,], -["features.roomdetails.impl_RoomDetails_2_en","",20588,], -["features.roomdetails.impl_RoomDetails_3_en","",20588,], -["features.roomdetails.impl_RoomDetails_4_en","",20588,], -["features.roomdetails.impl_RoomDetails_5_en","",20588,], -["features.roomdetails.impl_RoomDetails_6_en","",20588,], -["features.roomdetails.impl_RoomDetails_7_en","",20588,], -["features.roomdetails.impl_RoomDetails_8_en","",20588,], -["features.roomdetails.impl_RoomDetails_9_en","",20588,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20588,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20588,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20588,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20588,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20588,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20588,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20588,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20588,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20588,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20595,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20595,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20595,], +["features.roomdetails.impl_RoomDetails_0_en","",20595,], +["features.roomdetails.impl_RoomDetails_10_en","",20595,], +["features.roomdetails.impl_RoomDetails_11_en","",20595,], +["features.roomdetails.impl_RoomDetails_12_en","",20595,], +["features.roomdetails.impl_RoomDetails_13_en","",20595,], +["features.roomdetails.impl_RoomDetails_14_en","",20595,], +["features.roomdetails.impl_RoomDetails_15_en","",20595,], +["features.roomdetails.impl_RoomDetails_16_en","",20595,], +["features.roomdetails.impl_RoomDetails_17_en","",20595,], +["features.roomdetails.impl_RoomDetails_18_en","",20595,], +["features.roomdetails.impl_RoomDetails_19_en","",20595,], +["features.roomdetails.impl_RoomDetails_1_en","",20595,], +["features.roomdetails.impl_RoomDetails_20_en","",20595,], +["features.roomdetails.impl_RoomDetails_21_en","",20595,], +["features.roomdetails.impl_RoomDetails_22_en","",20595,], +["features.roomdetails.impl_RoomDetails_2_en","",20595,], +["features.roomdetails.impl_RoomDetails_3_en","",20595,], +["features.roomdetails.impl_RoomDetails_4_en","",20595,], +["features.roomdetails.impl_RoomDetails_5_en","",20595,], +["features.roomdetails.impl_RoomDetails_6_en","",20595,], +["features.roomdetails.impl_RoomDetails_7_en","",20595,], +["features.roomdetails.impl_RoomDetails_8_en","",20595,], +["features.roomdetails.impl_RoomDetails_9_en","",20595,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20595,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20595,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20595,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20595,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20595,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20595,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20595,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20595,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20595,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20588,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20588,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20588,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20588,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20588,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20588,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20588,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20588,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20588,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20588,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20588,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20595,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20595,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20595,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20595,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20595,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20595,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20595,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20595,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20595,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20595,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20595,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20588,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20588,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20588,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20595,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20595,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20595,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20588,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20588,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20595,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20588,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20588,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20588,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20588,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20588,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20588,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20595,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1155,16 +1155,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20588,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20595,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20588,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20595,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1174,118 +1174,118 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20588,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20588,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20588,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20595,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20595,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20595,], ["appicon.element_RoundIcon_en","",0,], ["appicon.enterprise_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20588,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20588,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20588,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20588,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20588,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20588,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20588,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20588,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20595,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20595,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20595,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20595,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20595,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20595,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20595,], ["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","",20588,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20595,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20588,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20588,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20588,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20588,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20588,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20588,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20588,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20588,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20588,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20588,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20588,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20588,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20588,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20588,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20588,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20595,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20595,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20595,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20595,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20595,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20595,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20595,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20595,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20595,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20595,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20595,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20595,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20595,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20595,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20595,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1308,37 +1308,38 @@ 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.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20588,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20588,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20588,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20588,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20588,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20588,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20588,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20588,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20588,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20588,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20588,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20588,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20588,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20588,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20588,], -["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20591,], -["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20591,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20595,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20595,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20595,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20595,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20595,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20595,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20595,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20595,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20595,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20595,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20595,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20595,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20595,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20595,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20595,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20595,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20595,], ["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",20588,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20588,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20588,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20588,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20588,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20588,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20588,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20588,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20588,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20588,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20588,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20595,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20595,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20595,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20595,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20595,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20595,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20595,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20595,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20595,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20598,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20595,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1348,106 +1349,106 @@ 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",20588,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20595,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20588,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20588,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20588,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20588,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20588,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20595,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20595,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20595,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20595,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20595,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20588,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20588,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20588,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20588,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20588,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20588,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20588,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20588,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20588,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20588,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20588,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20588,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20588,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20588,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20588,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20595,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20595,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20595,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20595,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20595,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20595,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20595,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20595,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20595,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20595,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20595,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20595,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20595,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20595,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20588,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20588,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20588,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20588,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20588,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20588,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20595,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20595,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20595,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20595,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20595,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20595,], ["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",20588,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20595,], ["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",20588,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20595,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20588,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20588,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20588,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20588,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20588,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20588,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20588,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20588,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20588,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20588,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20588,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20588,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20588,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20588,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20588,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20588,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20595,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20595,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20595,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20595,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20595,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20595,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20595,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20595,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20595,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20595,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20595,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20595,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20595,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20595,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20595,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20595,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20588,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20588,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20595,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20595,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1460,17 +1461,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20588,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20588,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20595,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20595,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20588,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20588,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20588,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20595,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20595,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20595,], ["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",20588,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20588,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20595,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20595,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1480,18 +1481,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20595,], ["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",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20595,], ["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,], @@ -1499,18 +1500,18 @@ 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",20588,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20595,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20588,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20588,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20595,], ["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",20588,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20595,], ["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,], @@ -1519,44 +1520,44 @@ 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",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20595,], ["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",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20595,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20595,], ["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","",20588,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20595,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20588,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20595,], ["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",20588,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20595,], ["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",20588,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20588,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20588,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20595,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20595,], ["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",20588,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20588,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20595,], ["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",20588,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20595,], ["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,], @@ -1565,8 +1566,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",20588,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20595,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20595,], ["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,], @@ -1581,8 +1582,8 @@ 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",20588,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20588,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20595,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1605,84 +1606,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20588,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20588,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20595,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20595,], ["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",20588,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20595,], ["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",20588,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20595,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20588,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20588,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20595,], ["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",20588,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20588,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20588,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20588,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20588,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20588,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20595,], ["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","",20588,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20595,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20588,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20588,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20588,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20588,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20595,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20595,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20595,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20595,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20588,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20595,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20588,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20595,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20588,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20588,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20588,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20588,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20588,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20588,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20588,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20588,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20588,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20588,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20588,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20588,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20588,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20595,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20595,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20595,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20595,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20595,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20595,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20595,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20595,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20595,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20595,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20595,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20595,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20595,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20588,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20588,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20595,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20595,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20588,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20595,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], From 0e2213a199a3981811712dc90b24ad7fedb23118 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 10:10:42 +0200 Subject: [PATCH 242/471] Fix public read receipts being sent by mistake (#6838) When returning to the chat screen from the room details one or a member's profile, `TimelineEvent.OnScrollFinished` will be called immediately, and this would read the default value for `isSendPublicReadReceiptsEnabled`, which is `true`. If you had public read receipts disabled, this is a mistake, and would send a public read receipt. Instead, what we want to do is wait until the updated value is emitted and use it to decide whether we want to send a public or private read receipt. --- .../impl/timeline/TimelinePresenter.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0b99c45e06..0681a0ff38 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -66,6 +66,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -137,9 +138,6 @@ class TimelinePresenter( val messageShieldDialogData: MutableState = remember { mutableStateOf(null) } val resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailurePresenter.present() - val isSendPublicReadReceiptsEnabled by remember { - sessionPreferencesStore.isSendPublicReadReceiptsEnabled() - }.collectAsState(initial = true) val renderReadReceipts by remember { sessionPreferencesStore.isRenderReadReceiptsEnabled() }.collectAsState(initial = true) @@ -171,12 +169,15 @@ class TimelinePresenter( newEventState.value = NewEventState.None } Timber.tag(tag).d("## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex}") - sessionCoroutineScope.sendReadReceiptIfNeeded( - firstVisibleIndex = event.firstIndex, - timelineItems = timelineItems, - lastReadReceiptId = lastReadReceiptId, - readReceiptType = if (isSendPublicReadReceiptsEnabled) ReceiptType.READ else ReceiptType.READ_PRIVATE, - ) + sessionCoroutineScope.launch { + val sendPublicReadReceipts = sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first() + sendReadReceiptIfNeeded( + firstVisibleIndex = event.firstIndex, + timelineItems = timelineItems, + lastReadReceiptId = lastReadReceiptId, + readReceiptType = if (sendPublicReadReceipts) ReceiptType.READ else ReceiptType.READ_PRIVATE, + ) + } } else { newEventState.value = NewEventState.None } From 1e67c2f77bd3177a490528f8bc092c53bad806cc Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 10:28:32 +0200 Subject: [PATCH 243/471] Move empty day separator filtering to a timeline post-processor (#6866) * Move empty day separator filtering to a timeline post-processor * Split `FilterPublicMembershipChangesPostProcessor` from `RoomBeginningPostProcessor` --- .../factories/TimelineItemsFactory.kt | 26 +-- .../factories/TimelineItemsFactoryTest.kt | 160 ------------------ .../matrix/impl/timeline/RustTimeline.kt | 20 ++- .../FilterEmptyDayPostProcessor.kt | 38 +++++ ...terPublicMembershipChangesPostProcessor.kt | 43 +++++ .../RoomBeginningPostProcessor.kt | 16 -- .../FilterEmptyDayPostProcessorTest.kt | 118 +++++++++++++ ...ublicMembershipChangesPostProcessorTest.kt | 75 ++++++++ .../RoomBeginningPostProcessorTest.kt | 89 ---------- 9 files changed, 292 insertions(+), 293 deletions(-) delete mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessor.kt create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessorTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index dc8bdddc92..7b369fe6b7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -16,7 +16,6 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -97,8 +96,7 @@ class TimelineItemsFactory( } } val result = timelineItemGrouper.group(newTimelineItemStates).toImmutableList() - val filteredResult = filterEmptyDaySeparators(result) - this._timelineItems.emit(filteredResult) + this._timelineItems.emit(result) } private suspend fun buildAndCacheItem( @@ -116,25 +114,3 @@ class TimelineItemsFactory( return timelineItem } } - -// Remove day separators for days with no events after the client-side event filtering -internal fun filterEmptyDaySeparators(items: List): ImmutableList { - return buildList { - var hasEventBefore = false - for (item in items) { - when (item) { - is TimelineItem.Event, is TimelineItem.GroupedEvents -> { - hasEventBefore = true - add(item) - } - is TimelineItem.Virtual if item.model is TimelineItemDaySeparatorModel -> { - if (hasEventBefore) { - add(item) - } - hasEventBefore = false - } - else -> add(item) - } - } - }.toImmutableList() -} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt deleted file mode 100644 index 8df8d34f56..0000000000 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryTest.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.messages.impl.timeline.factories - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.fixtures.aMessageEvent -import io.element.android.features.messages.impl.timeline.aTimelineItemDebugInfo -import io.element.android.features.messages.impl.timeline.aTimelineItemReactions -import io.element.android.features.messages.impl.timeline.model.ReadReceiptData -import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts -import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel -import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel -import io.element.android.libraries.designsystem.components.avatar.anAvatarData -import io.element.android.libraries.matrix.api.core.UniqueId -import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails -import io.element.android.libraries.matrix.test.AN_EVENT_ID -import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.core.FakeSendHandle -import kotlinx.collections.immutable.toImmutableList -import org.junit.Test - -class TimelineItemsFactoryTest { - private val anEvent = TimelineItem.Event( - id = UniqueId("event"), - eventId = AN_EVENT_ID, - senderId = A_USER_ID, - senderAvatar = anAvatarData(), - senderProfile = ProfileDetails.Ready(displayName = "User", displayNameAmbiguous = false, avatarUrl = null), - content = aMessageEvent().content, - reactionsState = aTimelineItemReactions(count = 0), - readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), - localSendState = LocalEventSendState.Sent(AN_EVENT_ID), - isEditable = false, - canBeRepliedTo = false, - inReplyTo = null, - threadInfo = null, - origin = null, - timelineItemDebugInfoProvider = { aTimelineItemDebugInfo() }, - messageShieldProvider = { null }, - sendHandleProvider = { FakeSendHandle() }, - forwarder = null, - forwarderProfile = null, - ) - - private fun aDaySeparator(date: String) = TimelineItem.Virtual( - id = UniqueId("day_$date"), - model = aTimelineItemDaySeparatorModel(date) - ) - - @Test - fun `filterEmptyDaySeparators keeps day separator with events after it`() { - val items = listOf( - anEvent, - aDaySeparator("Today"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(anEvent) - assertThat(result[1]).isEqualTo(aDaySeparator("Today")) - } - - @Test - fun `filterEmptyDaySeparators removes day separator with no events after it`() { - val items = listOf( - aDaySeparator("Today"), - aDaySeparator("Yesterday"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).isEmpty() - } - - @Test - fun `filterEmptyDaySeparators removes first day separator and keeps second when only second has events`() { - val items = listOf( - aDaySeparator("Today"), - anEvent, - aDaySeparator("Yesterday"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(anEvent) - assertThat(result[1]).isEqualTo(aDaySeparator("Yesterday")) - } - - @Test - fun `filterEmptyDaySeparators handles multiple day separators in a row with no events`() { - val items = listOf( - aDaySeparator("Today"), - aDaySeparator("Yesterday"), - aDaySeparator("Last week"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).isEmpty() - } - - @Test - fun `filterEmptyDaySeparators keeps all items when no day separators`() { - val items = listOf( - anEvent, - anEvent.copy(id = UniqueId("event2")), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(2) - } - - @Test - fun `filterEmptyDaySeparators handles grouped events after day separator`() { - val groupedEvents = TimelineItem.GroupedEvents( - id = UniqueId("grouped"), - events = listOf(anEvent).toImmutableList(), - aggregatedReadReceipts = emptyList().toImmutableList(), - ) - val items = listOf( - groupedEvents, - aDaySeparator("Today"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(groupedEvents) - assertThat(result[1]).isEqualTo(aDaySeparator("Today")) - } - - @Test - fun `filterEmptyDaySeparators removes day separator followed by non-event virtual item`() { - val readMarker = TimelineItem.Virtual( - id = UniqueId("readMarker"), - model = TimelineItemReadMarkerModel - ) - val items = listOf( - aDaySeparator("Today"), - readMarker, - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(1) - assertThat(result[0]).isEqualTo(readMarker) - } - - @Test - fun `filterEmptyDaySeparators keeps day separator when non-event virtual items are between separator and event`() { - val readMarker = TimelineItem.Virtual( - id = UniqueId("readMarker"), - model = TimelineItemReadMarkerModel - ) - val items = listOf( - anEvent, - readMarker, - aDaySeparator("Today"), - ) - val result = filterEmptyDaySeparators(items) - assertThat(result).hasSize(3) - assertThat(result[2]).isEqualTo(aDaySeparator("Today")) - } -} 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 016204e1b1..d44676d34a 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 @@ -38,6 +38,8 @@ import io.element.android.libraries.matrix.impl.room.location.into import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper +import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterEmptyDayPostProcessor +import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterPublicMembershipChangesPostProcessor import io.element.android.libraries.matrix.impl.timeline.postprocessor.LastForwardIndicatorsPostProcessor import io.element.android.libraries.matrix.impl.timeline.postprocessor.LoadingIndicatorsPostProcessor import io.element.android.libraries.matrix.impl.timeline.postprocessor.RoomBeginningPostProcessor @@ -84,7 +86,7 @@ private const val PAGINATION_SIZE = 50 class RustTimeline( private val inner: InnerTimeline, override val mode: Timeline.Mode, - private val systemClock: SystemClock, + systemClock: SystemClock, private val joinedRoom: JoinedRoom, private val coroutineScope: CoroutineScope, private val dispatcher: CoroutineDispatcher, @@ -123,6 +125,8 @@ class RustTimeline( private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock) private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode) private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode) + private val publicMembershipChangesPostProcessor = FilterPublicMembershipChangesPostProcessor() + private val emptyDayPostProcessor = FilterEmptyDayPostProcessor() private data class RoomTimelineInfo( val roomCreators: ImmutableList, @@ -245,11 +249,21 @@ class RustTimeline( items = items, isDm = isDm, roomCreator = roomCreators.firstOrNull(), - joinRule = joinRule, - isEncrypted = isEncrypted, hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad, ) } + // This should be the first post processor after room beginning. + .let { items -> + publicMembershipChangesPostProcessor.process( + items = items, + joinRule = joinRule, + isEncrypted = isEncrypted, + ) + } + // After removing public membership changes, we might end up with empty days, so we need to filter them out. + .let { items -> + emptyDayPostProcessor.process(items) + } .let { items -> loadingIndicatorsPostProcessor.process( items = items, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt new file mode 100644 index 0000000000..b7dceafa25 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.timeline.postprocessor + +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem + +/** + * Post-processor to filter out day separators for days that don't contain any events. + */ +class FilterEmptyDayPostProcessor { + /** + * Filters out day separators from [items] for days that don't contain any events. + */ + fun process(items: List): List = buildList { + // The timeline is ordered by descending timestamp, so events that happened during a day appear before the day separator for that day. + // We can use this to determine if a day separator should be kept or not. + var hasEvent = false + for (item in items) { + if (item is MatrixTimelineItem.Event) { + hasEvent = true + add(item) + } else if (item is MatrixTimelineItem.Virtual && item.virtual is VirtualTimelineItem.DayDivider) { + if (hasEvent) { + add(item) + hasEvent = false + } + } else { + add(item) + } + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessor.kt new file mode 100644 index 0000000000..806c9369bf --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessor.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.timeline.postprocessor + +import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent +import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent + +/** + * Post-processor to filter out public membership changes for non-encrypted, publicly joinable rooms. + */ +class FilterPublicMembershipChangesPostProcessor { + /** + * Filters out public membership changes from [items] if the room is publicly joinable and not encrypted. + */ + fun process( + items: List, + joinRule: JoinRule?, + isEncrypted: Boolean?, + ): List { + return if (joinRule !is JoinRule.Invite && isEncrypted == false) { + filterMembershipEvents(items) + } else { + items + } + } + + private fun filterMembershipEvents(items: List): List = items.filter { item -> + val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content ?: return@filter true + when (eventContent) { + is RoomMembershipContent -> eventContent.change != null && eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT) + is ProfileChangeContent -> false + else -> true + } + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt index ec6a7a5380..8991d26f9c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessor.kt @@ -9,12 +9,10 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.OtherState -import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent import io.element.android.libraries.matrix.api.timeline.item.event.StateContent @@ -27,31 +25,17 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) { items: List, isDm: Boolean, roomCreator: UserId?, - joinRule: JoinRule?, - isEncrypted: Boolean?, hasMoreToLoadBackwards: Boolean, ): List { return when { items.isEmpty() -> items mode == Timeline.Mode.PinnedEvents -> items - joinRule !is JoinRule.Invite && isEncrypted == false -> filterRoomMemberEvents(items) isDm -> processForDM(items, roomCreator) hasMoreToLoadBackwards -> items else -> processForRoom(items) } } - private fun filterRoomMemberEvents(items: List): List { - return items.filter { item -> - val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content - when (eventContent) { - is RoomMembershipContent -> eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT) - is ProfileChangeContent -> false - else -> true - } - } - } - private fun processForRoom(items: List): List { // No changes needed, timeline start item is already added by the SDK return items diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt new file mode 100644 index 0000000000..7a3a091f4e --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.timeline.postprocessor + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem +import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem +import org.junit.Test + +private const val TODAY = 1_779_779_967_000 +private const val YESTERDAY = TODAY - 24 * 60 * 60 * 1000 +private const val DAY_BEFORE_YESTERDAY = YESTERDAY - 24 * 60 * 60 * 1000 + +class FilterEmptyDayPostProcessorTest { + private val anEvent = MatrixTimelineItem.Event( + uniqueId = UniqueId("event"), + event = anEventTimelineItem(), + ) + + private fun aDaySeparator(timestmap: Long) = MatrixTimelineItem.Virtual( + uniqueId = UniqueId("day_$timestmap"), + virtual = VirtualTimelineItem.DayDivider(timestmap) + ) + + @Test + fun `filterEmptyDaySeparators keeps day separator with events after it`() { + val items = listOf( + anEvent, + aDaySeparator(TODAY), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(anEvent) + assertThat(result[1]).isEqualTo(aDaySeparator(TODAY)) + } + + @Test + fun `filterEmptyDaySeparators removes day separator with no events after it`() { + val items = listOf( + aDaySeparator(TODAY), + aDaySeparator(YESTERDAY), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).isEmpty() + } + + @Test + fun `filterEmptyDaySeparators removes first day separator and keeps second when only second has events`() { + val items = listOf( + aDaySeparator(TODAY), + anEvent, + aDaySeparator(YESTERDAY), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).hasSize(2) + assertThat(result[0]).isEqualTo(anEvent) + assertThat(result[1]).isEqualTo(aDaySeparator(YESTERDAY)) + } + + @Test + fun `filterEmptyDaySeparators handles multiple day separators in a row with no events`() { + val items = listOf( + aDaySeparator(TODAY), + aDaySeparator(YESTERDAY), + aDaySeparator(DAY_BEFORE_YESTERDAY), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).isEmpty() + } + + @Test + fun `filterEmptyDaySeparators keeps all items when no day separators`() { + val items = listOf( + anEvent, + anEvent.copy(uniqueId = UniqueId("event2")), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).hasSize(2) + } + + @Test + fun `filterEmptyDaySeparators removes day separator followed by non-event virtual item`() { + val readMarker = MatrixTimelineItem.Virtual( + uniqueId = UniqueId("readMarker"), + virtual = VirtualTimelineItem.ReadMarker + ) + val items = listOf( + aDaySeparator(TODAY), + readMarker, + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).hasSize(1) + assertThat(result[0]).isEqualTo(readMarker) + } + + @Test + fun `filterEmptyDaySeparators keeps day separator when non-event virtual items are between separator and event`() { + val readMarker = MatrixTimelineItem.Virtual( + uniqueId = UniqueId("readMarker"), + virtual = VirtualTimelineItem.ReadMarker + ) + val items = listOf( + anEvent, + readMarker, + aDaySeparator(TODAY), + ) + val result = FilterEmptyDayPostProcessor().process(items) + assertThat(result).hasSize(3) + assertThat(result[2]).isEqualTo(aDaySeparator(TODAY)) + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessorTest.kt new file mode 100644 index 0000000000..dd899baf5b --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterPublicMembershipChangesPostProcessorTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.timeline.postprocessor + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.room.join.JoinRule +import org.junit.Test + +class FilterPublicMembershipChangesPostProcessorTest { + @Test + fun `processor removes join, leave, and profile events in unencrypted public rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val expected = listOf( + roomCreateEvent, + messageEvent, + ) + val processor = FilterPublicMembershipChangesPostProcessor() + val processedItems = processor.process( + timelineItems, + joinRule = JoinRule.Public, + isEncrypted = false, + ) + assertThat(processedItems).isEqualTo(expected) + } + + @Test + fun `processor keeps all events in encrypted public rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val processor = FilterPublicMembershipChangesPostProcessor() + val processedItems = processor.process( + timelineItems, + joinRule = JoinRule.Public, + isEncrypted = true, + ) + assertThat(processedItems).isEqualTo(timelineItems) + } + + @Test + fun `processor keeps membership events in invite-only rooms`() { + val timelineItems = listOf( + roomCreateEvent, + roomCreatorJoinEvent, + otherMemberJoinEvent, + messageEvent, + otherMemberLeaveEvent, + profileChangeEvent, + ) + val processor = FilterPublicMembershipChangesPostProcessor() + val processedItems = processor.process( + timelineItems, + joinRule = JoinRule.Invite, + isEncrypted = null, + ) + assertThat(processedItems).isEqualTo(timelineItems) + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt index b562847d94..680422827c 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/RoomBeginningPostProcessorTest.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.A_USER_ID import org.junit.Test @@ -22,8 +21,6 @@ class RoomBeginningPostProcessorTest { items = emptyList(), isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEmpty() @@ -36,8 +33,6 @@ class RoomBeginningPostProcessorTest { items = listOf(messageEvent), isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(listOf(messageEvent)) @@ -50,8 +45,6 @@ class RoomBeginningPostProcessorTest { items = listOf(messageEvent), isDm = true, roomCreator = null, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(listOf(messageEvent)) @@ -69,8 +62,6 @@ class RoomBeginningPostProcessorTest { items = timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).containsExactly(timelineStartEvent) @@ -87,8 +78,6 @@ class RoomBeginningPostProcessorTest { items = timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false, ) assertThat(processedItems).isEqualTo(timelineItems) @@ -111,8 +100,6 @@ class RoomBeginningPostProcessorTest { timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = false ) assertThat(processedItems).isEqualTo(expected) @@ -129,8 +116,6 @@ class RoomBeginningPostProcessorTest { timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = true ) assertThat(processedItems).isEmpty() @@ -146,8 +131,6 @@ class RoomBeginningPostProcessorTest { timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = true ) assertThat(processedItems).isEmpty() @@ -164,80 +147,8 @@ class RoomBeginningPostProcessorTest { timelineItems, isDm = true, roomCreator = A_USER_ID, - joinRule = null, - isEncrypted = null, hasMoreToLoadBackwards = true ) assertThat(processedItems).isEqualTo(listOf(otherMemberJoinEvent)) } - - @Test - fun `processor removes join, leave, and profile events in unencrypted public rooms`() { - val timelineItems = listOf( - roomCreateEvent, - roomCreatorJoinEvent, - otherMemberJoinEvent, - messageEvent, - otherMemberLeaveEvent, - profileChangeEvent, - ) - val expected = listOf( - roomCreateEvent, - messageEvent, - ) - val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process( - timelineItems, - isDm = false, - roomCreator = A_USER_ID, - joinRule = JoinRule.Public, - isEncrypted = false, - hasMoreToLoadBackwards = false - ) - assertThat(processedItems).isEqualTo(expected) - } - - @Test - fun `processor keeps all events in encrypted public rooms`() { - val timelineItems = listOf( - roomCreateEvent, - roomCreatorJoinEvent, - otherMemberJoinEvent, - messageEvent, - otherMemberLeaveEvent, - profileChangeEvent, - ) - val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process( - timelineItems, - isDm = false, - roomCreator = A_USER_ID, - joinRule = JoinRule.Public, - isEncrypted = true, - hasMoreToLoadBackwards = false - ) - assertThat(processedItems).isEqualTo(timelineItems) - } - - @Test - fun `processor keeps membership events in invite-only rooms`() { - val timelineItems = listOf( - roomCreateEvent, - roomCreatorJoinEvent, - otherMemberJoinEvent, - messageEvent, - otherMemberLeaveEvent, - profileChangeEvent, - ) - val processor = RoomBeginningPostProcessor(Timeline.Mode.Live) - val processedItems = processor.process( - timelineItems, - isDm = false, - roomCreator = A_USER_ID, - joinRule = JoinRule.Invite, - isEncrypted = null, - hasMoreToLoadBackwards = false - ) - assertThat(processedItems).isEqualTo(timelineItems) - } } From e1f968390a35e10f7eb45e28fd10698cb8b0b156 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Tue, 26 May 2026 11:56:45 +0300 Subject: [PATCH 244/471] fix unused import --- .../android/libraries/slashcommands/impl/CommandExecutorTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt b/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt index 800cdecfb3..c0f2ce89c2 100644 --- a/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt +++ b/libraries/slashcommands/impl/src/test/kotlin/io/element/android/libraries/slashcommands/impl/CommandExecutorTest.kt @@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.timeline.MsgType import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom From 87b3a5d2f0f3cffe1a19a882985dc1bf05492530 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 12:19:26 +0200 Subject: [PATCH 245/471] Add better logs to track token update failures (#6859) 1. Make some logs use `info` log level instead of `debug`, so they appear in most user's bug reports. 2. Make the anonymized tokens even harder to reverse. 3. Detect when the tokens we should be saving match the current ones, as that's an error. --- .../matrix/impl/RustClientSessionDelegate.kt | 16 ++++++++++++++-- .../matrix/impl/RustMatrixClientFactory.kt | 2 +- .../android/libraries/matrix/impl/util/Token.kt | 3 ++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt index c776ca8522..a5c69bf831 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustClientSessionDelegate.kt @@ -65,10 +65,22 @@ class RustClientSessionDelegate( // This always runs on a background thread, so we *can* do blocking calls here, although we should avoid doing heavy work override fun saveSessionInKeychain(session: Session) { + Timber.tag(loggerTag.value).i("Saving new session info for user ${session.userId} after a token refresh") runCatchingExceptions { val existingData = runBlocking { sessionStore.getSession(session.userId) } ?: return + + if (existingData.accessToken == session.accessToken) { + Timber.tag(loggerTag.value).e("Access token is the same as the one already stored, this should not happen after a token refresh!") + return + } + + if (existingData.refreshToken == session.refreshToken) { + Timber.tag(loggerTag.value).e("Refresh token is the same as the one already stored, this should not happen after a token refresh!") + return + } + val (anonymizedAccessToken, anonymizedRefreshToken) = session.anonymizedTokens() - Timber.tag(loggerTag.value).d( + Timber.tag(loggerTag.value).i( "Saving new session data with token: access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'. " + "Was token valid: ${existingData.isTokenValid}" ) @@ -79,7 +91,7 @@ class RustClientSessionDelegate( sessionPaths = existingData.getSessionPaths(), ) runBlocking { sessionStore.updateData(newData) } - Timber.tag(loggerTag.value).d("Saved new session data with access token: '$anonymizedAccessToken'.") + Timber.tag(loggerTag.value).i("Saved new session data.") }.onFailure { Timber.tag(loggerTag.value).e(it, "Failed to save new session data.") } 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 6757edf16c..c22a8b9454 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 @@ -131,7 +131,7 @@ class RustMatrixClientFactory( analyticsService = analyticsService, workManagerScheduler = workManagerScheduler, ).also { - Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") + Timber.tag("RustMatrixClient").i("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Token.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Token.kt index 815e134cf2..f5df21008b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Token.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Token.kt @@ -16,7 +16,8 @@ private val sha256 by lazy { MessageDigest.getInstance("SHA-256") } @OptIn(ExperimentalStdlibApi::class) private fun anonymizeToken(token: String): String { - return sha256.digest(token.toByteArray()).toHexString() + // Only keep the first 32 chars (16 bytes) of the hashed token to avoid displaying too much information. + return sha256.digest(token.toByteArray()).toHexString().take(32) } fun SessionData?.anonymizedTokens(): Pair { From 5aea8852407c2b8f49e630274135af9be2effff4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 10:21:34 +0000 Subject: [PATCH 246/471] Update dependency io.element.android:element-call-embedded to v0.20.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a328218d79..882d66f358 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -235,7 +235,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.19.4" +element_call_embedded = "io.element.android:element-call-embedded:0.20.0" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } From b34473a88cc5682619e7cffddcaffd354dabc33e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 10:21:44 +0000 Subject: [PATCH 247/471] Update roborazzi to v1.62.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a328218d79..52cf4ca9ae 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.60.0" +roborazzi = "1.62.0" # Jetbrain datetime = "0.8.0" From 4a907688937c66049dab0e66de388fb4b33e2c41 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 26 May 2026 15:25:48 +0200 Subject: [PATCH 248/471] Read map tiler custom style from matrix .well-known file Closes #6844 --- .../MapTilerTileServerStyleUriBuilder.kt | 21 +++- .../api/internal/TileServerStyleUriBuilder.kt | 14 ++- .../MapTilerTileServerStyleUriBuilderTest.kt | 30 +++++- .../impl/common/ui/MapBottomSheetScaffold.kt | 28 ++++-- .../impl/share/ShareLocationPresenter.kt | 7 ++ .../location/impl/share/ShareLocationState.kt | 2 + .../impl/share/ShareLocationStateProvider.kt | 6 ++ .../location/impl/share/ShareLocationView.kt | 1 + .../impl/show/ShowLocationPresenter.kt | 9 ++ .../location/impl/show/ShowLocationState.kt | 2 + .../impl/show/ShowLocationStateProvider.kt | 6 ++ .../location/impl/show/ShowLocationView.kt | 1 + .../impl/share/ShareLocationPresenterTest.kt | 96 ++++++++++--------- .../show/DefaultShowLocationEntryPointTest.kt | 2 + .../impl/show/ShowLocationPresenterTest.kt | 29 +++++- gradle/libs.versions.toml | 2 +- .../libraries/matrix/api/MatrixClient.kt | 5 + .../libraries/matrix/impl/RustMatrixClient.kt | 6 ++ .../matrix/impl/pushers/RustPushersService.kt | 3 +- .../libraries/matrix/test/FakeMatrixClient.kt | 5 + 20 files changed, 211 insertions(+), 64 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index d119dc7eb2..761adb25cf 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -25,8 +25,23 @@ internal class MapTilerTileServerStyleUriBuilder( darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID, ) - override fun build(darkMode: Boolean): String { - val mapId = if (darkMode) darkMapId else lightMapId - return "$baseUrl/$mapId/style.json?key=$apiKey" + override fun build( + customMapStyleUrl: String?, + darkMode: Boolean, + ): String { + return buildString { + if (customMapStyleUrl.isNullOrBlank()) { + val mapId = if (darkMode) darkMapId else lightMapId + append(baseUrl) + append("/") + append(mapId) + append("/") + append("style.json") + } else { + append(customMapStyleUrl.removeSuffix("/")) + } + append("?key=") + append(apiKey) + } } } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt index 17b620eec9..762d580f8e 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -19,21 +19,25 @@ import io.element.android.compound.theme.ElementTheme */ interface TileServerStyleUriBuilder { fun build( + customMapStyleUrl: String?, darkMode: Boolean, ): String } -fun TileServerStyleUriBuilder(): TileServerStyleUriBuilder = MapTilerTileServerStyleUriBuilder() - /** * Provides and remembers a style URI for a MapLibre compatible tile server. * * Used for rendering dynamic maps. */ @Composable -fun rememberTileStyleUrl(): String { +fun rememberTileStyleUrl( + customMapStyleUrl: String?, +): String { val darkMode = !ElementTheme.isLightTheme - return remember(darkMode) { - TileServerStyleUriBuilder().build(darkMode) + return remember(darkMode, customMapStyleUrl) { + MapTilerTileServerStyleUriBuilder().build( + customMapStyleUrl = customMapStyleUrl, + darkMode = darkMode, + ) } } diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt index d5c6521258..2e07204594 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -22,14 +22,40 @@ class MapTilerTileServerStyleUriBuilderTest { @Test fun `light map uri`() { assertThat( - builder.build(darkMode = false) + builder.build( + customMapStyleUrl = null, + darkMode = false, + ) ).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey") } @Test fun `dark map uri`() { assertThat( - builder.build(darkMode = true) + builder.build( + customMapStyleUrl = null, + darkMode = true, + ) ).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey") } + + @Test + fun `custom map uri light`() { + assertThat( + builder.build( + customMapStyleUrl = "https://custom.url/style.json", + darkMode = false, + ) + ).isEqualTo("https://custom.url/style.json?key=anApiKey") + } + + @Test + fun `custom map uri dark`() { + assertThat( + builder.build( + customMapStyleUrl = "https://custom.url/style.json", + darkMode = true, + ) + ).isEqualTo("https://custom.url/style.json?key=anApiKey") + } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt index 13c30c28eb..5f09bcedb2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/MapBottomSheetScaffold.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp @@ -44,9 +45,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.common.MapDefaults +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.BottomSheetScaffold +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState import org.maplibre.compose.map.MapOptions @@ -63,6 +66,7 @@ import kotlin.math.roundToInt * - Updating camera position padding based on sheet height * - Rendering the MaplibreMap with proper ornament positioning * + * @param customMapStyleUrl Optional custom style URL for the map * @param modifier Modifier for the root layout * @param scaffoldState State for the bottom sheet scaffold * @param cameraState The camera state for the map @@ -79,6 +83,7 @@ import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) @Composable fun MapBottomSheetScaffold( + customMapStyleUrl: AsyncData, modifier: Modifier = Modifier, scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState( bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded) @@ -130,13 +135,22 @@ fun MapBottomSheetScaffold( val ornamentOptions = mapOptions.ornamentOptions.copy(padding = sheetPadding) val mapOptions = mapOptions.copy(ornamentOptions = ornamentOptions) Box { - MaplibreMap( - options = mapOptions, - baseStyle = BaseStyle.Uri(rememberTileStyleUrl()), - modifier = Modifier.fillMaxSize(), - cameraState = cameraState, - content = mapContent, - ) + when (customMapStyleUrl) { + is AsyncData.Success -> { + MaplibreMap( + options = mapOptions, + baseStyle = BaseStyle.Uri(rememberTileStyleUrl(customMapStyleUrl.data)), + modifier = Modifier.fillMaxSize(), + cameraState = cameraState, + content = mapContent, + ) + } + else -> { + Box(modifier = Modifier.fillMaxSize()) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } + } + } overlayContent(sheetPadding) } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index a1e45cfea2..9f4c8333b8 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -35,6 +36,7 @@ import io.element.android.features.location.impl.common.toDialogState import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.messages.api.MessageComposerContext 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.runUpdatingState import io.element.android.libraries.core.extensions.flatMap @@ -86,6 +88,10 @@ class ShareLocationPresenter( } val startLiveLocationAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val currentUser by client.userProfile.collectAsState() + val customMapStyleUrl by produceState(AsyncData.Loading()) { + // Ignore errors + value = AsyncData.Success(client.getMapStyleUrl().getOrNull()) + } val sendLiveLocationPermissions by room.permissionsAsState(SendLiveLocationPermissions.DEFAULT) { perms -> perms.sendLiveLocationPermissions() } @@ -161,6 +167,7 @@ class ShareLocationPresenter( } return ShareLocationState( + customMapStyleUrl = customMapStyleUrl, currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt index 68598cba04..9e78345bab 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt @@ -10,10 +10,12 @@ package io.element.android.features.location.impl.share import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList data class ShareLocationState( + val customMapStyleUrl: AsyncData, val currentUser: MatrixUser, val dialogState: Dialog, val trackUserLocation: Boolean, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt index ae1b765b6b..e7942b12df 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.share import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.persistentListOf @@ -76,10 +77,14 @@ class ShareLocationStateProvider : PreviewParameterProvider hasLocationPermission = true, canShareLiveLocation = true, ), + aShareLocationState( + customMapStyleUrl = AsyncData.Loading(), + ), ) } fun aShareLocationState( + customMapStyleUrl: AsyncData = AsyncData.Success(null), currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")), dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None, trackUserPosition: Boolean = false, @@ -90,6 +95,7 @@ fun aShareLocationState( eventSink: (ShareLocationEvent) -> Unit = {}, ): ShareLocationState { return ShareLocationState( + customMapStyleUrl = customMapStyleUrl, currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index e20ee3a7a5..985c6c8bdc 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -117,6 +117,7 @@ fun ShareLocationView( } MapBottomSheetScaffold( + customMapStyleUrl = state.customMapStyleUrl, cameraState = cameraState, modifier = modifier, scaffoldState = scaffoldState, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 207b4b01dd..bdb6dd26b8 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -33,6 +33,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsP import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.toDialogState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.core.meta.BuildMeta @@ -40,6 +41,7 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode 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.MatrixClient import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.getBestName import io.element.android.libraries.matrix.api.room.joinedRoomMembers @@ -58,6 +60,7 @@ class ShowLocationPresenter( private val buildMeta: BuildMeta, private val dateFormatter: DateFormatter, private val stringProvider: StringProvider, + private val client: MatrixClient, private val joinedRoom: JoinedRoom, private val liveLocationShareManager: ActiveLiveLocationShareManager, ) : Presenter { @@ -78,6 +81,11 @@ class ShowLocationPresenter( mutableStateOf(LocationConstraintsDialogState.None) } + val customMapStyleUrl by produceState(AsyncData.Loading()) { + // Ignore errors + value = AsyncData.Success(client.getMapStyleUrl().getOrNull()) + } + LaunchedEffect(permissionsState.permissions) { if (permissionsState.isAnyGranted) { dialogState = LocationConstraintsDialogState.None @@ -188,6 +196,7 @@ class ShowLocationPresenter( } return ShowLocationState( + customMapStyleUrl = customMapStyleUrl, dialogState = dialogState, locationShares = locationShares, focusedLocation = focusedLocation, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index 720697cbf7..478a3bd730 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.ui.LocationMarkerData +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.core.UserId @@ -18,6 +19,7 @@ import io.element.android.libraries.matrix.api.room.location.AssetType import kotlinx.collections.immutable.ImmutableList data class ShowLocationState( + val customMapStyleUrl: AsyncData, val isLive: Boolean, val dialogState: LocationConstraintsDialogState, val locationShares: ImmutableList, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index 1c7b9a3160..85eacd3ce4 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE @@ -41,12 +42,16 @@ class ShowLocationStateProvider : PreviewParameterProvider { hasLocationPermission = true, isTrackMyLocation = true, ), + aShowLocationState( + customMapStyleUrl = AsyncData.Loading(), + ), ) } private const val APP_NAME = "ApplicationName" fun aShowLocationState( + customMapStyleUrl: AsyncData = AsyncData.Success(null), isLive: Boolean = false, constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None, locationShares: List = listOf(aLocationShareItem(isLive = isLive)), @@ -57,6 +62,7 @@ fun aShowLocationState( eventSink: (ShowLocationEvent) -> Unit = {}, ): ShowLocationState { return ShowLocationState( + customMapStyleUrl = customMapStyleUrl, dialogState = constraintsDialogState, locationShares = locationShares.toImmutableList(), focusedLocation = focusedLocation, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 6766fa6424..80ffaabedd 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -100,6 +100,7 @@ fun ShowLocationView( } } MapBottomSheetScaffold( + customMapStyleUrl = state.customMapStyleUrl, sheetDragHandle = if (state.isSheetDraggable) { { BottomSheetDefaults.DragHandle() } } else { diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index 1bc4fce586..5be96a50c5 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -15,6 +15,7 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences 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 im.vector.app.features.analytics.plan.Composer @@ -75,7 +76,10 @@ class ShareLocationPresenterTest { private val fakeMessageComposerContext = FakeMessageComposerContext() private val fakeLocationActions = FakeLocationActions() private val fakeBuildMeta = aBuildMeta(applicationName = "app name") - private val fakeMatrixClient = FakeMatrixClient(sessionId = A_USER_ID) + private val fakeMatrixClient = FakeMatrixClient( + sessionId = A_USER_ID, + getMapStyleUrlResult = { Result.success(null) }, + ) private val durationFormatter = FakeDurationFormatter() @@ -85,6 +89,7 @@ class ShareLocationPresenterTest { locationActions: FakeLocationActions = fakeLocationActions, liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), liveLocationStore: LiveLocationStore = createLiveLocationStore(sessionId = joinedRoom.sessionId), + client: FakeMatrixClient = fakeMatrixClient, ): ShareLocationPresenter = ShareLocationPresenter( permissionsPresenterFactory = { fakePermissionsPresenter }, room = joinedRoom, @@ -93,7 +98,7 @@ class ShareLocationPresenterTest { messageComposerContext = fakeMessageComposerContext, locationActions = locationActions, buildMeta = fakeBuildMeta, - client = fakeMatrixClient, + client = client, durationFormatter = durationFormatter, liveLocationShareManager = liveLocationShareManager, liveLocationStore = liveLocationStore, @@ -110,14 +115,31 @@ class ShareLocationPresenterTest { val shareLocationPresenter = createShareLocationPresenter() shareLocationPresenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() + assertThat(state.customMapStyleUrl.isLoading()).isFalse() assertThat(state.trackUserLocation).isTrue() assertThat(state.hasLocationPermission).isTrue() assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None)) } } + @Test + fun `present - non-null customMapStyleUrl`() = runTest { + val shareLocationPresenter = createShareLocationPresenter( + client = FakeMatrixClient( + sessionId = A_USER_ID, + getMapStyleUrlResult = { Result.success("aUrl") }, + ) + ) + shareLocationPresenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.customMapStyleUrl.isLoading()).isTrue() + val finalState = awaitItem() + assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl") + } + } + @Test fun `initial state with permissions partially granted and location enabled`() = runTest { val shareLocationPresenter = createShareLocationPresenter() @@ -131,8 +153,7 @@ class ShareLocationPresenterTest { moleculeFlow(RecompositionMode.Immediate) { shareLocationPresenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isTrue() assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None)) @@ -152,8 +173,7 @@ class ShareLocationPresenterTest { moleculeFlow(RecompositionMode.Immediate) { shareLocationPresenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.dialogState).isEqualTo( @@ -173,8 +193,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.dialogState).isEqualTo( @@ -195,8 +214,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.dialogState).isEqualTo( @@ -216,8 +234,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isTrue() initialState.eventSink(ShareLocationEvent.StopTrackingUserLocation) @@ -237,8 +254,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.dialogState).isEqualTo( ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale) ) @@ -260,7 +276,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(ShareLocationEvent.RequestPermissions) // Wait for dialog to be dismissed @@ -282,8 +298,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(ShareLocationEvent.OpenAppSettings) val settingsOpenedState = awaitItem() @@ -304,8 +319,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() assertThat(initialState.dialogState).isEqualTo( ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled) ) @@ -337,8 +351,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val durationDialogState = awaitItem() @@ -358,8 +371,7 @@ class ShareLocationPresenterTest { ) presenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val dialogState = awaitItem() @@ -385,8 +397,7 @@ class ShareLocationPresenterTest { ) presenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) awaitItem() @@ -413,8 +424,7 @@ class ShareLocationPresenterTest { ) presenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val disclaimerState = awaitItem() @@ -444,8 +454,7 @@ class ShareLocationPresenterTest { ) presenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val durationState = awaitItem() @@ -472,8 +481,7 @@ class ShareLocationPresenterTest { ) presenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.InitiateLiveLocationShare) val dialogState = awaitItem() @@ -504,8 +512,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() // Dismiss initial dialog initialState.eventSink(ShareLocationEvent.DismissDialog) val dismissedState = awaitItem() @@ -539,8 +546,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink( ShareLocationEvent.ShareStaticLocation( @@ -593,7 +599,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - val initialState = awaitItem() + val initialState = awaitFirstItem() initialState.eventSink( ShareLocationEvent.ShareStaticLocation( @@ -641,8 +647,7 @@ class ShareLocationPresenterTest { ) shareLocationPresenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() state.eventSink(ShareLocationEvent.StartLiveLocationShare(duration = 1.hours)) advanceUntilIdle() assert(startShareLambda).isCalledOnce().with( @@ -659,8 +664,7 @@ class ShareLocationPresenterTest { timelineMode = Timeline.Mode.Live, ) shareLocationPresenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() assertThat(state.canShareLiveLocation).isTrue() } } @@ -671,11 +675,15 @@ class ShareLocationPresenterTest { timelineMode = Timeline.Mode.Thread(A_THREAD_ID), ) shareLocationPresenter.test { - skipItems(1) - val state = awaitItem() + val state = awaitFirstItem() assertThat(state.canShareLiveLocation).isFalse() } } + + private suspend fun ReceiveTurbine.awaitFirstItem(): T { + skipItems(2) + return awaitItem() + } } private fun createLiveLocationStore( diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index 985dcc1f9c..8f55510128 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -19,6 +19,7 @@ import io.element.android.features.location.impl.common.permissions.FakePermissi import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.services.analytics.test.FakeAnalyticsService @@ -48,6 +49,7 @@ class DefaultShowLocationEntryPointTest { dateFormatter = FakeDateFormatter(), stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, + client = FakeMatrixClient(), liveLocationShareManager = FakeActiveLiveLocationShareManager(), ) }, diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index c1f9f6487e..da3cea88f6 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -22,10 +22,13 @@ import io.element.android.features.location.impl.common.permissions.PermissionsS import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.test.FakeActiveLiveLocationShareManager import io.element.android.libraries.dateformatter.test.FakeDateFormatter +import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.LiveLocationShare +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.location.aLiveLocationShare @@ -61,6 +64,7 @@ class ShowLocationPresenterTest { ), locationActions: FakeLocationActions = fakeLocationActions, joinedRoom: JoinedRoom = FakeJoinedRoom(), + client: MatrixClient = FakeMatrixClient(), liveLocationShareManager: FakeActiveLiveLocationShareManager = FakeActiveLiveLocationShareManager(), ) = ShowLocationPresenter( mode = mode, @@ -70,6 +74,7 @@ class ShowLocationPresenterTest { dateFormatter = fakeDateFormatter, stringProvider = FakeStringProvider(), joinedRoom = joinedRoom, + client = client, liveLocationShareManager = liveLocationShareManager, ) @@ -85,11 +90,28 @@ class ShowLocationPresenterTest { val presenter = createShowLocationPresenter() presenter.test { val initialState = awaitItem() + assertThat(initialState.customMapStyleUrl.isLoading()).isTrue() assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.isTrackMyLocation).isFalse() } } + @Test + fun `present - non-null customMapStyleUrl`() = runTest { + val shareLocationPresenter = createShowLocationPresenter( + client = FakeMatrixClient( + sessionId = A_USER_ID, + getMapStyleUrlResult = { Result.success("aUrl") }, + ) + ) + shareLocationPresenter.test { + val state = awaitItem() + assertThat(state.customMapStyleUrl.isLoading()).isTrue() + val finalState = awaitItem() + assertThat(finalState.customMapStyleUrl.dataOrNull()).isEqualTo("aUrl") + } + } + @Test fun `emits initial state location permission denied once`() = runTest { fakePermissionsPresenter.givenState( @@ -146,8 +168,13 @@ class ShowLocationPresenterTest { fun `centers on user location`() = runTest { fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) - val presenter = createShowLocationPresenter() + val presenter = createShowLocationPresenter( + client = FakeMatrixClient( + getMapStyleUrlResult = { Result.success(null) } + ) + ) presenter.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.isTrackMyLocation).isFalse() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a328218d79..170504f0fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -179,7 +179,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.20" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.26" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } 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 bd7b1399e1..836459f8c7 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 @@ -221,6 +221,11 @@ interface MatrixClient { */ suspend fun performDatabaseVacuum(): Result + /** + * Returns the URL of the map style configured on the server, if any. + */ + suspend fun getMapStyleUrl(): Result + /** * Resets the cached client `well-known` config by the SDK. */ 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 05e52605dc..bec20aedf2 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 @@ -808,6 +808,12 @@ class RustMatrixClient( } } + override suspend fun getMapStyleUrl(): Result = withContext(sessionDispatcher) { + runCatchingExceptions { + innerClient.tileServer()?.mapStyleUrl + } + } + override suspend fun resetWellKnownConfig(): Result { return runCatchingExceptions { Timber.d("Resetting well-known config for session $sessionId") diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt index 860993d4bc..03a4ca3281 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt @@ -44,7 +44,8 @@ class RustPushersService( appDisplayName = setHttpPusherData.appDisplayName, deviceDisplayName = setHttpPusherData.deviceDisplayName, profileTag = setHttpPusherData.profileTag, - lang = setHttpPusherData.lang + lang = setHttpPusherData.lang, + append = false, ) } .mapFailure { it.mapClientException() } 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 af28dc37e4..3278eae855 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 @@ -115,6 +115,7 @@ class FakeMatrixClient( private val addRecentEmojiLambda: (String) -> Result = { Result.success(Unit) }, private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result = { _, _ -> lambdaError() }, private val performDatabaseVacuumLambda: () -> Result = { lambdaError() }, + private val getMapStyleUrlResult: () -> Result = { lambdaError() }, private val getDatabaseSizesLambda: () -> Result = { lambdaError() }, private val resetWellKnownConfigLambda: () -> Result = { lambdaError() }, ) : MatrixClient { @@ -373,6 +374,10 @@ class FakeMatrixClient( return performDatabaseVacuumLambda() } + override suspend fun getMapStyleUrl(): Result = simulateLongTask { + getMapStyleUrlResult() + } + override suspend fun canLinkNewDevice(): Result = simulateLongTask { return canLinkNewDeviceResult() } From f6bdf51f6d360f81ba32490e9bded21b8eaab6b3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 26 May 2026 14:33:42 +0000 Subject: [PATCH 249/471] Update screenshots --- ...features.location.impl.share_ShareLocationView_Day_9_en.png | 3 +++ ...atures.location.impl.share_ShareLocationView_Night_9_en.png | 3 +++ .../features.location.impl.show_ShowLocationView_Day_8_en.png | 3 +++ ...features.location.impl.show_ShowLocationView_Night_8_en.png | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png new file mode 100644 index 0000000000..f4ff1a611e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc36d1f05a6035dd4775dda0592d5557d45314c24d638738401eb910b6ba719c +size 21719 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png new file mode 100644 index 0000000000..213a75dce9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.share_ShareLocationView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5198742a388a2bd04985fe6b9f035de3878125607fe3b554209b0eb5c4590db3 +size 19935 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png new file mode 100644 index 0000000000..e01e90f7d0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dea2fa898a6f9b921eca0931d7380814b31ba858b88c90ba1fbab6332b4c8ebb +size 20722 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png new file mode 100644 index 0000000000..547e7ba364 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9f64bc6bb5956459f8943c05bbf0aa6ee9e97e3103713f34d49c013f86376a9 +size 18878 From dd48582313563aee3ad8a11e0178084a65dfb291 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 19:27:35 +0200 Subject: [PATCH 250/471] Remove DI-generated code from Kover reports (#6834) * Remove DI-generated code from Kover reports * Add more regexes for DI-generated code to exclude --- plugins/src/main/kotlin/extension/KoverExtension.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/src/main/kotlin/extension/KoverExtension.kt b/plugins/src/main/kotlin/extension/KoverExtension.kt index 5d6b1ddabb..c851a25ab9 100644 --- a/plugins/src/main/kotlin/extension/KoverExtension.kt +++ b/plugins/src/main/kotlin/extension/KoverExtension.kt @@ -126,6 +126,14 @@ fun Project.setupKover() { "io.element.android.tests.konsist.failures", // Copied from Appyx "io.element.android.libraries.architecture.appyx.SafeChildrenTransitionScope", + // DI-generated classes + "io.element.android.x.di.*Impl", + "io.element.android.x.di.*Impls", + "io.element.android.x.di.*Mirror", + "io.element.android.x.di.*Factory", + $$"io.element.android.*$Metro*", + $$"io.element.android.*$Factory*", + $$"io.element.android.*$Impl*", ) annotatedBy( "androidx.compose.ui.tooling.preview.Preview", From d7af6c0d0ad01dc3cb93d149cea3e673bbae5812 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 26 May 2026 19:28:23 +0200 Subject: [PATCH 251/471] Use a raw key for the SDK stores for new sessions (#6835) * Use a raw key for the SDK stores for new sessions - Add `ClientSecret` to tell apart the legacy passphrases from the raw keys and centralise parsing/formatting. - Use the `ClientSecret` to set the passphrase/key in the `SqliteStoreBuilder`. --- .../libraries/matrix/impl/ClientSecret.kt | 66 +++++++++++++++++++ .../matrix/impl/RustMatrixClientFactory.kt | 8 ++- .../auth/RustMatrixAuthenticationService.kt | 28 ++++---- ...Generator.kt => DefaultSecretGenerator.kt} | 17 +++-- .../matrix/impl/keys/PassphraseGenerator.kt | 17 ----- .../matrix/impl/keys/SecretGenerator.kt | 30 +++++++++ .../matrix/impl/storage/SqliteStoreBuilder.kt | 36 +++++++++- .../impl/auth/FakePassphraseGenerator.kt | 18 ----- .../matrix/impl/auth/FakeSecretGenerator.kt | 21 ++++++ .../RustMatrixAuthenticationServiceTest.kt | 2 +- .../keys/DefaultPassphraseGeneratorTest.kt | 24 ------- .../impl/keys/DefaultSecretGeneratorTest.kt | 35 ++++++++++ .../impl/storage/FakeSqliteStoreBuilder.kt | 3 +- .../DefaultAnalyticsColdStartWatcher.kt | 5 +- 14 files changed, 220 insertions(+), 90 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/{DefaultPassphraseGenerator.kt => DefaultSecretGenerator.kt} (50%) delete mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/PassphraseGenerator.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt delete mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakePassphraseGenerator.kt create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt delete mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGeneratorTest.kt create mode 100644 libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt new file mode 100644 index 0000000000..25d040e15b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl + +import okio.ByteString.Companion.decodeHex + +/** + * Represents a client secret used to encrypt/decrypt data from databases, which can be either a passphrase or a raw key. + */ +sealed interface ClientSecret { + /** + * A passphrase that can be used to derive a key for encryption/decryption. + */ + data class Passphrase(val value: String) : ClientSecret { + override fun formattedAsString(): String = toString() + } + + /** + * A raw key that can be directly used for encryption/decryption. The key is represented as a byte array, and is formatted as a string in the form of + * `x'...'` where the bytes are encoded as hex characters. + */ + data class RawKey(val bytes: ByteArray) : ClientSecret { + override fun formattedAsString() = "x'${bytes.toHexString()}'" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + return bytes.contentEquals((other as RawKey).bytes) + } + + override fun hashCode(): Int { + return bytes.contentHashCode() + } + + override fun toString(): String { + return formattedAsString() + } + } + + /** + * Format the client secret as a string that can be parsed back with [fromString]. + * For a passphrase, this is just the passphrase value. For a raw key, this is the hex-encoded representation of the key formatted as `x'...'`. + */ + fun formattedAsString(): String + + companion object { + /** + * Parse a string representation of a client secret, which can be either a passphrase or a raw key formatted as `x'...'`. + */ + fun fromString(secret: String): ClientSecret { + val regex = Regex("^x'([0-9a-fA-F]+)'$") + val rawKeyMatch = regex.matchEntire(secret) + return if (rawKeyMatch != null) { + RawKey(rawKeyMatch.groupValues[1].decodeHex().toByteArray()) + } else { + Passphrase(secret) + } + } + } +} 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 c22a8b9454..9a7452aa5f 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 @@ -75,9 +75,11 @@ class RustMatrixClientFactory( ) suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { + // This secret is called 'passphrase' for historical reasons, but it can be a raw key or an actual passphrase + val clientSecret = sessionData.passphrase?.let(ClientSecret::fromString) val client = getBaseClientBuilder( sessionPaths = sessionData.getSessionPaths(), - passphrase = sessionData.passphrase, + clientSecret = clientSecret, slidingSyncType = ClientBuilderSlidingSync.Restored, ) .homeserverUrl(sessionData.homeserverUrl) @@ -137,13 +139,13 @@ class RustMatrixClientFactory( internal suspend fun getBaseClientBuilder( sessionPaths: SessionPaths, - passphrase: String?, + clientSecret: ClientSecret?, slidingSyncType: ClientBuilderSlidingSync, ): ClientBuilder { return clientBuilderProvider.provide() .run { sqliteStoreBuilderProvider.provide(sessionPaths) - .passphrase(passphrase) + .secret(clientSecret) .setupClientBuilder(this) } .setSessionDelegate(sessionDelegate) 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 692edf79ae..812a0dd6fe 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 @@ -30,12 +30,13 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync +import io.element.android.libraries.matrix.impl.ClientSecret import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep import io.element.android.libraries.matrix.impl.exception.mapClientException -import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator +import io.element.android.libraries.matrix.impl.keys.SecretGenerator import io.element.android.libraries.matrix.impl.mapper.toSessionData import io.element.android.libraries.matrix.impl.paths.SessionPaths import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory @@ -65,7 +66,7 @@ class RustMatrixAuthenticationService( private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, private val rustMatrixClientFactory: RustMatrixClientFactory, - private val passphraseGenerator: PassphraseGenerator, + private val secretGenerator: SecretGenerator, private val oAuthConfigurationProvider: OAuthConfigurationProvider, private val enterpriseService: EnterpriseService, ) : MatrixAuthenticationService { @@ -74,7 +75,7 @@ class RustMatrixAuthenticationService( // Passphrase which will be used for new sessions. Existing sessions will use the passphrase // stored in the SessionData. - private val pendingPassphrase = getDatabasePassphrase() + private val pendingKey by lazy { getDatabaseKey() } // Need to keep a copy of the current session path to eventually delete it. // Ideally it would be possible to get the sessionPath from the Client to avoid doing this. @@ -115,12 +116,9 @@ class RustMatrixAuthenticationService( } } - private fun getDatabasePassphrase(): String? { - val passphrase = passphraseGenerator.generatePassphrase() - if (passphrase != null) { - Timber.w("New sessions will be encrypted with a passphrase") - } - return passphrase + private fun getDatabaseKey(): ClientSecret { + Timber.d("New sessions will be encrypted with a raw key") + return secretGenerator.generateKey() } override suspend fun setHomeserver(homeserver: String): Result = @@ -159,7 +157,7 @@ class RustMatrixAuthenticationService( .toSessionData( isTokenValid = true, loginType = LoginType.PASSWORD, - passphrase = pendingPassphrase, + passphrase = pendingKey.formattedAsString(), sessionPaths = currentSessionPaths, ) val matrixClient = rustMatrixClientFactory.create(client) @@ -231,7 +229,7 @@ class RustMatrixAuthenticationService( val sessionData = externalSession.toSessionData( isTokenValid = true, loginType = LoginType.PASSWORD, - passphrase = pendingPassphrase, + passphrase = pendingKey.formattedAsString(), sessionPaths = currentSessionPaths, ) @@ -324,7 +322,7 @@ class RustMatrixAuthenticationService( val sessionData = client.session().toSessionData( isTokenValid = true, loginType = LoginType.OIDC, - passphrase = pendingPassphrase, + passphrase = pendingKey.formattedAsString(), sessionPaths = currentSessionPaths, ) val matrixClient = rustMatrixClientFactory.create(client) @@ -389,7 +387,7 @@ class RustMatrixAuthenticationService( .toSessionData( isTokenValid = true, loginType = LoginType.QR, - passphrase = pendingPassphrase, + passphrase = pendingKey.formattedAsString(), sessionPaths = emptySessionPaths, ) val matrixClient = rustMatrixClientFactory.create(client) @@ -422,7 +420,7 @@ class RustMatrixAuthenticationService( return rustMatrixClientFactory .getBaseClientBuilder( sessionPaths = sessionPaths, - passphrase = pendingPassphrase, + clientSecret = pendingKey, slidingSyncType = ClientBuilderSlidingSync.Discovered, ) .config() @@ -454,7 +452,7 @@ class RustMatrixAuthenticationService( return rustMatrixClientFactory .getBaseClientBuilder( sessionPaths = sessionPaths, - passphrase = pendingPassphrase, + clientSecret = pendingKey, slidingSyncType = ClientBuilderSlidingSync.Discovered, ) .serverNameOrHomeserverUrl(baseUrlOrServerName) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGenerator.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt similarity index 50% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGenerator.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt index d13ff7779e..042dae36d9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGenerator.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt @@ -11,15 +11,20 @@ package io.element.android.libraries.matrix.impl.keys import android.util.Base64 import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.matrix.impl.ClientSecret import java.security.SecureRandom -private const val SECRET_SIZE = 256 - @ContributesBinding(AppScope::class) -class DefaultPassphraseGenerator : PassphraseGenerator { - override fun generatePassphrase(): String? { - val key = ByteArray(size = SECRET_SIZE) +class DefaultSecretGenerator : SecretGenerator { + override fun generatePassphrase(size: Int): ClientSecret.Passphrase? { + val key = ByteArray(size = size) SecureRandom().nextBytes(key) - return Base64.encodeToString(key, Base64.NO_PADDING or Base64.NO_WRAP) + return ClientSecret.Passphrase(Base64.encodeToString(key, Base64.NO_PADDING or Base64.NO_WRAP)) + } + + override fun generateKey(size: Int): ClientSecret.RawKey { + val key = ByteArray(size = size) + SecureRandom().nextBytes(key) + return ClientSecret.RawKey(key) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/PassphraseGenerator.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/PassphraseGenerator.kt deleted file mode 100644 index e0f925fa28..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/PassphraseGenerator.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.impl.keys - -interface PassphraseGenerator { - /** - * Generate a passphrase to encrypt the databases of a session. - * Return null to not encrypt the databases. - */ - fun generatePassphrase(): String? -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt new file mode 100644 index 0000000000..4699f2a720 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2024, 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.keys + +import io.element.android.libraries.matrix.impl.ClientSecret + +private const val PASSPHRASE_SIZE = 256 +private const val KEY_SIZE = 32 + +interface SecretGenerator { + /** + * Generate a passphrase to encrypt the databases. + * @param size the size of the passphrase in bytes, before encoding. The default value is 256 bytes. + * @return either a random passphrase or `null` to not encrypt the databases. + */ + fun generatePassphrase(size: Int = PASSPHRASE_SIZE): ClientSecret.Passphrase? + + /** + * Generate a key to encrypt the databases. + * @param size the size of the key in bytes. The default value is 32 bytes. + * @return a random key. + */ + fun generateKey(size: Int = KEY_SIZE): ClientSecret.RawKey +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt index 84f124868e..1e5b8e037c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt @@ -9,12 +9,23 @@ package io.element.android.libraries.matrix.impl.storage import io.element.android.libraries.core.data.ByteUnit import io.element.android.libraries.core.data.megaBytes +import io.element.android.libraries.matrix.impl.ClientSecret import io.element.android.libraries.matrix.impl.paths.SessionPaths import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.SqliteStoreBuilder as SdkSqliteStoreBuilder +/** + * Abstraction over the SDK's [SdkSqliteStoreBuilder] to allow configuring it with a ClientSecret and to hide the SDK from the rest of the codebase. + */ interface SqliteStoreBuilder { - fun passphrase(passphrase: String?): SqliteStoreBuilder + /** + * Configure the builder with a [ClientSecret], if provided. If the [clientSecret] is null, the databases will not be encrypted. + */ + fun secret(clientSecret: ClientSecret?): SqliteStoreBuilder + + /** + * Configure the provided [clientBuilder] with the configured [SdkSqliteStoreBuilder] and return it. + */ fun setupClientBuilder(clientBuilder: ClientBuilder): ClientBuilder } @@ -26,8 +37,15 @@ class RustSqliteStoreBuilder( cachePath = sessionPaths.cacheDirectory.absolutePath, ).journalSizeLimit(25.megaBytes.into(ByteUnit.BYTES).toUInt()) - override fun passphrase(passphrase: String?): SqliteStoreBuilder { - inner = inner.passphrase(passphrase) + override fun secret(clientSecret: ClientSecret?): SqliteStoreBuilder { + when (clientSecret) { + null -> Unit + is ClientSecret.Passphrase -> inner = inner.passphrase(clientSecret.value) + is ClientSecret.RawKey -> { + // Ensure the key is 32 bytes long, as required by the SDK + inner = inner.key(clientSecret.keyOfSize(32)) + } + } return this } @@ -35,3 +53,15 @@ class RustSqliteStoreBuilder( return clientBuilder.sqliteStore(this.inner) } } + +private fun ClientSecret.RawKey.keyOfSize(size: Int): ByteArray { + return if (bytes.size == 32) { + bytes + } else if (bytes.size < 32) { + // If the key is shorter than 32 bytes, pad it with zeros + bytes + ByteArray(32 - bytes.size) + } else { + // Otherwise, take the first 32 bytes of the key + bytes.copyOfRange(0, 32) + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakePassphraseGenerator.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakePassphraseGenerator.kt deleted file mode 100644 index a089920422..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakePassphraseGenerator.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.impl.auth - -import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator -import io.element.android.libraries.matrix.test.A_PASSPHRASE - -class FakePassphraseGenerator( - private val passphrase: () -> String? = { A_PASSPHRASE } -) : PassphraseGenerator { - override fun generatePassphrase(): String? = passphrase() -} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt new file mode 100644 index 0000000000..6bd995348a --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2024, 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.auth + +import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.matrix.impl.keys.SecretGenerator +import io.element.android.libraries.matrix.test.A_PASSPHRASE + +class FakeSecretGenerator( + private val passphrase: (Int) -> String? = { A_PASSPHRASE }, + private val key: (Int) -> ByteArray = { ByteArray(it) { 0 } }, +) : SecretGenerator { + override fun generatePassphrase(size: Int): ClientSecret.Passphrase? = passphrase(size)?.let { ClientSecret.Passphrase(it) } + override fun generateKey(size: Int): ClientSecret.RawKey = ClientSecret.RawKey(key(size)) +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt index 7f422acfcf..2b9080195f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationServiceTest.kt @@ -66,7 +66,7 @@ class RustMatrixAuthenticationServiceTest { coroutineDispatchers = testCoroutineDispatchers(), sessionStore = sessionStore, rustMatrixClientFactory = rustMatrixClientFactory, - passphraseGenerator = FakePassphraseGenerator(), + secretGenerator = FakeSecretGenerator(), oAuthConfigurationProvider = OAuthConfigurationProvider( buildMeta = aBuildMeta(), oAuthRedirectUrlProvider = FakeOAuthRedirectUrlProvider(), diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGeneratorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGeneratorTest.kt deleted file mode 100644 index 465d920074..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultPassphraseGeneratorTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2024, 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.impl.keys - -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class DefaultPassphraseGeneratorTest { - @Test - fun `check that generated passphrase has the expected length`() { - val passphraseGenerator = DefaultPassphraseGenerator() - val passphrase = passphraseGenerator.generatePassphrase() - assertThat(passphrase!!.length).isEqualTo(342) - } -} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt new file mode 100644 index 0000000000..5fa2b263a1 --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Element Creations Ltd. + * Copyright 2024, 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.keys + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.impl.ClientSecret +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DefaultSecretGeneratorTest { + @Test + fun `check that generated passphrase has the expected length`() { + val secretGenerator = DefaultSecretGenerator() + val passphrase = secretGenerator.generatePassphrase(256) + assertThat(passphrase).isInstanceOf(ClientSecret.Passphrase::class.java) + // Size after Base64 encoding should be 4/3 of the original size, without padding + assertThat(passphrase!!.value).hasLength(342) + } + + @Test + fun `check that generated key has the expected length`() { + val secretGenerator = DefaultSecretGenerator() + val key = secretGenerator.generateKey(123) + assertThat(key).isInstanceOf(ClientSecret.RawKey::class.java) + assertThat(key.bytes).hasLength(123) + } +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt index 2f12587f5a..c33a67790b 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt @@ -7,10 +7,11 @@ package io.element.android.libraries.matrix.impl.storage +import io.element.android.libraries.matrix.impl.ClientSecret import org.matrix.rustcomponents.sdk.ClientBuilder class FakeSqliteStoreBuilder : SqliteStoreBuilder { - override fun passphrase(passphrase: String?): SqliteStoreBuilder = this + override fun secret(clientSecret: ClientSecret?): SqliteStoreBuilder = this override fun setupClientBuilder(clientBuilder: ClientBuilder): ClientBuilder { return clientBuilder diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt index e596bc35a5..a9647ba3a1 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt @@ -56,8 +56,9 @@ class DefaultAnalyticsColdStartWatcher( override fun onRoomListVisible() { if (isColdStart.getAndSet(false)) { - Timber.d("Room list is visible, finishing cold start check") - analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart) + analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart) { + Timber.d("Room list is visible, finishing cold start check. Elapsed: ${it.duration}") + } } } } From 8fd5072ba55f4a92a681b94ba9e13ebc21a4bb08 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 00:46:10 +0000 Subject: [PATCH 252/471] Update dependency org.maplibre.compose:maplibre-compose to v0.13.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2abf57b90..391a97bfc9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -210,7 +210,7 @@ telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = " statemachine = "com.freeletics.flowredux:compose:1.2.2" maplibre = "org.maplibre.gl:android-sdk:13.1.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" -maplibre_compose = "org.maplibre.compose:maplibre-compose:0.12.1" +maplibre_compose = "org.maplibre.compose:maplibre-compose:0.13.0" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" opusencoder = "io.element.android:opusencoder:1.2.0" zxing_cpp = "io.github.zxing-cpp:android:3.0.2" From ecfeb52424578591e69ff49983bf73c39f831276 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 09:22:28 +0200 Subject: [PATCH 253/471] Update dependency androidx.compose:compose-bom to v2026.05.01 (#6888) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2abf57b90..514dbd19d4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ camera = "1.6.1" work = "2.11.2" # Compose -compose_bom = "2026.05.00" +compose_bom = "2026.05.01" # Coroutines coroutines = "1.11.0" From 6984ab03933a8c294d2255a3e9637f20211c705b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 27 May 2026 09:54:27 +0200 Subject: [PATCH 254/471] Use string template instead of multiple append invocations. --- .../api/internal/MapTilerTileServerStyleUriBuilder.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index 761adb25cf..b580ea4824 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -32,16 +32,11 @@ internal class MapTilerTileServerStyleUriBuilder( return buildString { if (customMapStyleUrl.isNullOrBlank()) { val mapId = if (darkMode) darkMapId else lightMapId - append(baseUrl) - append("/") - append(mapId) - append("/") - append("style.json") + append("$baseUrl/$mapId/style.json") } else { append(customMapStyleUrl.removeSuffix("/")) } - append("?key=") - append(apiKey) + append("?key=$apiKey") } } } From c846f4eee29fae2bdd4af13c8a9053a6e1da0d8a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 27 May 2026 09:59:32 +0200 Subject: [PATCH 255/471] `customMapStyleUrl` should end with `style.json` and should not have any trailing slash. --- .../location/api/internal/MapTilerTileServerStyleUriBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt index b580ea4824..054805d1ab 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -34,7 +34,7 @@ internal class MapTilerTileServerStyleUriBuilder( val mapId = if (darkMode) darkMapId else lightMapId append("$baseUrl/$mapId/style.json") } else { - append(customMapStyleUrl.removeSuffix("/")) + append(customMapStyleUrl) } append("?key=$apiKey") } From f37222e2d8e4ac464312fefe55e3f94cd136ef6e Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 26 May 2026 23:17:32 +0200 Subject: [PATCH 256/471] change(permissions) : allow to change ShareLiveLocation permission --- .../impl/permissions/ChangeRoomPermissionsPresenter.kt | 2 ++ .../impl/permissions/ChangeRoomPermissionsState.kt | 2 ++ .../impl/permissions/ChangeRoomPermissionsView.kt | 1 + .../impl/permissions/ChangeRoomPermissionsPresenterTest.kt | 1 + .../android/libraries/matrix/impl/room/JoinedRustRoom.kt | 2 ++ 5 files changed, 8 insertions(+) diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt index 552f1d0ec6..3023124e9e 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenter.kt @@ -47,6 +47,7 @@ class ChangeRoomPermissionsPresenter( RoomPermissionsSection.MessagesAndContent -> persistentListOf( RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, + RoomPermissionType.SHARE_LIVE_LOCATION, ) RoomPermissionsSection.ManageMembers -> persistentListOf( RoomPermissionType.INVITE, @@ -114,6 +115,7 @@ class ChangeRoomPermissionsPresenter( RoomPermissionType.ROOM_AVATAR -> currentPermissions?.copy(roomAvatar = powerLevel) RoomPermissionType.ROOM_TOPIC -> currentPermissions?.copy(roomTopic = powerLevel) RoomPermissionType.SPACE_MANAGE_ROOMS -> currentPermissions?.copy(spaceChild = powerLevel) + RoomPermissionType.SHARE_LIVE_LOCATION -> currentPermissions?.copy(beaconInfo = powerLevel, beacon = powerLevel) } } is ChangeRoomPermissionsEvent.Save -> coroutineScope.save() diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt index 535f2b4a71..b6b2ca0bda 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsState.kt @@ -65,6 +65,7 @@ data class ChangeRoomPermissionsState( RoomPermissionType.ROOM_AVATAR -> currentPermissions.roomAvatar RoomPermissionType.ROOM_TOPIC -> currentPermissions.roomTopic RoomPermissionType.SPACE_MANAGE_ROOMS -> currentPermissions.spaceChild + RoomPermissionType.SHARE_LIVE_LOCATION -> maxOf(currentPermissions.beacon, currentPermissions.beaconInfo) } } } @@ -104,4 +105,5 @@ enum class RoomPermissionType { ROOM_AVATAR, ROOM_TOPIC, SPACE_MANAGE_ROOMS, + SHARE_LIVE_LOCATION, } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt index 529df0d50d..bb702ed90a 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsView.kt @@ -127,6 +127,7 @@ private fun titleForType(type: RoomPermissionType): String = when (type) { RoomPermissionType.ROOM_AVATAR -> stringResource(R.string.screen_room_change_permissions_room_avatar) RoomPermissionType.ROOM_TOPIC -> stringResource(R.string.screen_room_change_permissions_room_topic) RoomPermissionType.SPACE_MANAGE_ROOMS -> stringResource(R.string.screen_room_change_permissions_manage_space_rooms) + RoomPermissionType.SHARE_LIVE_LOCATION -> stringResource(R.string.screen_room_change_permissions_live_location) } @PreviewsDayNight diff --git a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt index 72b2b8b5e7..c143b7c969 100644 --- a/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt +++ b/features/rolesandpermissions/impl/src/test/kotlin/io/element/android/features/rolesandpermissions/impl/permissions/ChangeRoomPermissionsPresenterTest.kt @@ -66,6 +66,7 @@ class ChangeRoomPermissionsPresenterTest { assertThat(itemsBySection[RoomPermissionsSection.MessagesAndContent]).containsExactly( RoomPermissionType.SEND_EVENTS, RoomPermissionType.REDACT_EVENTS, + RoomPermissionType.SHARE_LIVE_LOCATION, ) assertThat(itemsBySection[RoomPermissionsSection.ManageMembers]).containsExactly( RoomPermissionType.INVITE, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt index e75091eadc..1b02c03e30 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/JoinedRustRoom.kt @@ -421,6 +421,8 @@ class JoinedRustRoom( roomAvatar = roomPowerLevelsValues.roomAvatar, roomTopic = roomPowerLevelsValues.roomTopic, spaceChild = roomPowerLevelsValues.spaceChild, + beacon = roomPowerLevelsValues.beacon, + beaconInfo = roomPowerLevelsValues.beaconInfo, ) innerRoom.applyPowerLevelChanges(changes) } From f36916e5e7c0e4fe317a285103dfb4fc46a22660 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 27 May 2026 11:03:45 +0200 Subject: [PATCH 257/471] Fix test compilation --- .../libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt index 57ffcddb37..43c36f7bfb 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt @@ -73,6 +73,7 @@ class FakeFfiClient( deviceDisplayName: String, profileTag: String?, lang: String, + append: Boolean, ) = Unit override suspend fun deletePusher(identifiers: PusherIdentifiers) = Unit From 22054875717cc3aab92419e7aae90a36aa5b7564 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 11:38:49 +0200 Subject: [PATCH 258/471] Merge pull request #6893 from element-hq/renovate/roborazzi Update roborazzi to v1.63.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 514dbd19d4..d3b2314b33 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.62.0" +roborazzi = "1.63.0" # Jetbrain datetime = "0.8.0" From 2e42e78307998d69f15f9cf4b6334974f231e29c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 12:16:17 +0200 Subject: [PATCH 259/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.05.26 (#6885) * Update dependency org.matrix.rustcomponents:sdk-android to v26.05.26 * Fix API break * Fix test compilation --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Benoit Marty --- gradle/libs.versions.toml | 2 +- .../android/libraries/matrix/api/pusher/SetHttpPusherData.kt | 1 + .../libraries/matrix/impl/pushers/RustPushersService.kt | 3 ++- .../libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt | 1 + .../libraries/matrix/impl/pushers/RustPushersServiceTest.kt | 4 +++- .../android/libraries/push/impl/DefaultPusherSubscriber.kt | 3 ++- .../libraries/push/impl/DefaultPusherSubscriberTest.kt | 1 + 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d3b2314b33..b526c0ab69 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -179,7 +179,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.20" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.26" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/SetHttpPusherData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/SetHttpPusherData.kt index 8a557f4b70..97a3cabd20 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/SetHttpPusherData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/pusher/SetHttpPusherData.kt @@ -17,4 +17,5 @@ data class SetHttpPusherData( val profileTag: String?, val lang: String, val defaultPayload: String, + val append: Boolean, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt index 860993d4bc..2e2a36c449 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersService.kt @@ -44,7 +44,8 @@ class RustPushersService( appDisplayName = setHttpPusherData.appDisplayName, deviceDisplayName = setHttpPusherData.deviceDisplayName, profileTag = setHttpPusherData.profileTag, - lang = setHttpPusherData.lang + lang = setHttpPusherData.lang, + append = setHttpPusherData.append, ) } .mapFailure { it.mapClientException() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt index 57ffcddb37..43c36f7bfb 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClient.kt @@ -73,6 +73,7 @@ class FakeFfiClient( deviceDisplayName: String, profileTag: String?, lang: String, + append: Boolean, ) = Unit override suspend fun deletePusher(identifiers: PusherIdentifiers) = Unit diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersServiceTest.kt index 61b2965cdd..b58c2985ff 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/pushers/RustPushersServiceTest.kt @@ -48,6 +48,7 @@ private fun aSetHttpPusherData( deviceDisplayName: String = "deviceDisplayName", profileTag: String = "profileTag", lang: String = "lang", + append: Boolean = false, ) = SetHttpPusherData( pushKey = pushKey, appId = appId, @@ -56,7 +57,8 @@ private fun aSetHttpPusherData( appDisplayName = appDisplayName, deviceDisplayName = deviceDisplayName, profileTag = profileTag, - lang = lang + lang = lang, + append = append, ) private fun aUnsetHttpPusherData( diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt index 39f9d3acd6..35482aa164 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriber.kt @@ -83,7 +83,8 @@ class DefaultPusherSubscriber( // TODO getDeviceInfoUseCase.execute().displayName().orEmpty() deviceDisplayName = "MyDevice", url = gateway, - defaultPayload = createDefaultPayload(pushClientSecret.getSecretForUser(userId)) + defaultPayload = createDefaultPayload(pushClientSecret.getSecretForUser(userId)), + append = false, ) /** diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt index dd2cb24841..8cb1e2a7f6 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/DefaultPusherSubscriberTest.kt @@ -103,6 +103,7 @@ class DefaultPusherSubscriberTest { profileTag = DEFAULT_PUSHER_FILE_TAG + "_", lang = "en", defaultPayload = "{\"cs\":\"$A_SECRET\"}", + append = false, ), ) ) From e115d620cae5bf3ef9a9e5172bf4a83ac75211df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 27 May 2026 13:19:41 +0200 Subject: [PATCH 260/471] Fix API breaks and an issue with the location changing not moving the camera --- .../impl/common/ui/UserLocationPuck.kt | 37 ++++++++++++------- .../service/LiveLocationSharingService.kt | 10 +++-- .../impl/share/ShareLocationPresenter.kt | 2 +- .../location/impl/share/ShareLocationView.kt | 4 +- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt index 589ed87c6f..6f47903c9c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.impl.common.MapDefaults +import kotlinx.coroutines.flow.SharingStarted import org.maplibre.compose.camera.CameraState import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.LocationPuck @@ -23,6 +24,9 @@ import org.maplibre.compose.location.UserLocationState import org.maplibre.compose.location.rememberAndroidLocationProvider import org.maplibre.compose.location.rememberNullLocationProvider import org.maplibre.compose.location.rememberUserLocationState +import org.maplibre.spatialk.units.Bearing +import org.maplibre.spatialk.units.extensions.inDegrees +import org.maplibre.spatialk.units.extensions.meters import kotlin.time.Duration.Companion.seconds @Composable @@ -31,22 +35,28 @@ fun UserLocationPuck( locationState: UserLocationState, trackUserLocation: Boolean, ) { - LocationTrackingEffect( - locationState = locationState, - enabled = trackUserLocation, - ) { - val finalPosition = cameraState.position.copy( - target = currentLocation.position, - bearing = currentLocation.bearing ?: cameraState.position.bearing, - zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) - ) - cameraState.animateTo(finalPosition) - } val location = locationState.location if (location != null) { + // Moved inside this block so it correctly tracks the updated locationState value + LocationTrackingEffect( + locationState = locationState, + enabled = trackUserLocation, + ) { + val newTarget = currentLocation.location?.position?.value + val newBearing = currentLocation.orientation?.orientation?.value?.clockwiseRotationTo(Bearing.North)?.inDegrees + if (newTarget != null || newBearing != null) { + val finalPosition = cameraState.position.copy( + target = newTarget ?: cameraState.position.target, + bearing = newBearing ?: cameraState.position.bearing, + zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) + ) + cameraState.animateTo(finalPosition) + } + } + LocationPuck( idPrefix = "user-location", - locationState = locationState, + location = locationState.location, cameraState = cameraState, accuracyThreshold = Float.POSITIVE_INFINITY, showBearingAccuracy = false, @@ -74,7 +84,8 @@ fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState rememberAndroidLocationProvider( updateInterval = 5.seconds, desiredAccuracy = DesiredAccuracy.High, - minDistanceMeters = 5f, + minDistance = 5.meters, + sharingStarted = SharingStarted.Eagerly, ) } return rememberUserLocationState(locationProvider) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt index 4451febb19..aba45f30c1 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -36,6 +36,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import org.maplibre.compose.location.AndroidLocationProvider import org.maplibre.compose.location.DesiredAccuracy +import org.maplibre.spatialk.units.extensions.inMeters +import org.maplibre.spatialk.units.extensions.meters import timber.log.Timber import kotlin.time.Duration.Companion.seconds import io.element.android.features.location.api.Location as ApiLocation @@ -91,7 +93,7 @@ class LiveLocationSharingService : Service() { val locationProvider = AndroidLocationProvider( context = applicationContext, updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds, - minDistanceMeters = minDistanceMeters.toFloat(), + minDistance = minDistanceMeters.meters, desiredAccuracy = DesiredAccuracy.Balanced, coroutineScope = coroutineScope ) @@ -100,9 +102,9 @@ class LiveLocationSharingService : Service() { .filterNotNull() .map { location -> ApiLocation( - lat = location.position.latitude, - lon = location.position.longitude, - accuracy = location.accuracy.toFloat(), + lat = location.position.value.latitude, + lon = location.position.value.longitude, + accuracy = location.position.accuracy?.inMeters?.toFloat() ?: 0f, ) } .onEach(coordinator::dispatch) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index a1e45cfea2..bb2ed77131 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -94,8 +94,8 @@ class ShareLocationPresenter( fun checkLocationConstraints() { // No need to check SendLiveLocationPermissions here val locationConstraints = checkLocationConstraints(permissionsState, locationActions, SendLiveLocationPermissions.GRANTED) - dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState()) trackUserPosition = locationConstraints is LocationConstraintsCheck.Success + dialogState = ShareLocationState.Dialog.Constraints(locationConstraints.toDialogState()) } suspend fun computeLiveLocationDialogState(): ShareLocationState.Dialog { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index e20ee3a7a5..7320911990 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -222,8 +222,8 @@ private fun BottomSheetContent( state.eventSink( ShareLocationEvent.ShareStaticLocation( location = Location( - lat = userLocation.position.latitude, - lon = userLocation.position.longitude + lat = userLocation.position.value.latitude, + lon = userLocation.position.value.longitude ), isPinned = false ) From 1e00353f297e50d41b81ab0bec3d7bb0ab844ed4 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 27 May 2026 17:25:51 +0200 Subject: [PATCH 261/471] Avoid SQLCipher key derivation (#6774) * Avoid SQLCipher key derivation by manually providing the generated as a raw key: this needs a series of DB re-key operations, performed as part of a 'dummy' DB migration * Set key generation length to 32 bytes in `RandomSecretPassphraseProvider`. * Use `ClientSecret` for handling the keys for SQLCipher. Move `ClientSecret` to `:libraries:androidutils` so it's shared. --- .../androidutils/crypto}/ClientSecret.kt | 16 ++++--- .../cachestore/impl/di/CacheStoreModule.kt | 19 ++++++-- .../impl/src/main/sqldelight/migrations/1.sqm | 8 ++++ .../impl/src/main/sqldelight/migrations/2.sqm | 2 + .../encrypteddb/SqlCipherDriverFactory.kt | 40 +++++++++++++---- ...eProvider.kt => DatabaseSecretProvider.kt} | 13 ++++-- ...der.kt => RandomDatabaseSecretProvider.kt} | 26 ++++++----- .../encrypteddb/utils/ReplaceDatabaseKey.kt | 45 +++++++++++++++++++ .../matrix/impl/RustMatrixClientFactory.kt | 1 + .../auth/RustMatrixAuthenticationService.kt | 2 +- .../impl/keys/DefaultSecretGenerator.kt | 2 +- .../matrix/impl/keys/SecretGenerator.kt | 2 +- .../matrix/impl/storage/SqliteStoreBuilder.kt | 2 +- .../matrix/impl/auth/FakeSecretGenerator.kt | 2 +- .../impl/keys/DefaultSecretGeneratorTest.kt | 2 +- .../impl/storage/FakeSqliteStoreBuilder.kt | 2 +- .../push/impl/history/di/PushHistoryModule.kt | 19 ++++++-- .../impl/src/main/sqldelight/migrations/2.sqm | 2 + .../impl/di/SessionStorageModule.kt | 19 ++++++-- .../src/main/sqldelight/migrations/11.sqm | 2 + .../DefaultAnalyticsColdStartWatcher.kt | 2 +- 21 files changed, 185 insertions(+), 43 deletions(-) rename libraries/{matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl => androidutils/src/main/kotlin/io/element/android/libraries/androidutils/crypto}/ClientSecret.kt (81%) create mode 100644 libraries/cachestore/impl/src/main/sqldelight/migrations/1.sqm create mode 100644 libraries/cachestore/impl/src/main/sqldelight/migrations/2.sqm rename libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/{PassphraseProvider.kt => DatabaseSecretProvider.kt} (51%) rename libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/{RandomSecretPassphraseProvider.kt => RandomDatabaseSecretProvider.kt} (56%) create mode 100644 libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/utils/ReplaceDatabaseKey.kt create mode 100644 libraries/push/impl/src/main/sqldelight/migrations/2.sqm create mode 100644 libraries/session-storage/impl/src/main/sqldelight/migrations/11.sqm diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/crypto/ClientSecret.kt similarity index 81% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt rename to libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/crypto/ClientSecret.kt index 25d040e15b..d07d302f39 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/ClientSecret.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/crypto/ClientSecret.kt @@ -5,9 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.impl - -import okio.ByteString.Companion.decodeHex +package io.element.android.libraries.androidutils.crypto /** * Represents a client secret used to encrypt/decrypt data from databases, which can be either a passphrase or a raw key. @@ -17,7 +15,7 @@ sealed interface ClientSecret { * A passphrase that can be used to derive a key for encryption/decryption. */ data class Passphrase(val value: String) : ClientSecret { - override fun formattedAsString(): String = toString() + override fun formattedAsString(): String = value } /** @@ -57,10 +55,18 @@ sealed interface ClientSecret { val regex = Regex("^x'([0-9a-fA-F]+)'$") val rawKeyMatch = regex.matchEntire(secret) return if (rawKeyMatch != null) { - RawKey(rawKeyMatch.groupValues[1].decodeHex().toByteArray()) + RawKey(rawKeyMatch.groupValues[1].hexToByteArray()) } else { Passphrase(secret) } } + + /** + * Create a [ClientSecret] from raw bytes, which will be treated as a raw key. + */ + fun fromRawBytes(bytes: ByteArray): ClientSecret = when (bytes.size) { + 32 -> RawKey(bytes) + else -> Passphrase(bytes.toHexString()) + } } } diff --git a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt index 05fa3d9d97..89de82a08f 100644 --- a/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt +++ b/libraries/cachestore/impl/src/main/kotlin/io/element/android/libraries/cachestore/impl/di/CacheStoreModule.kt @@ -16,7 +16,9 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.cachestore.impl.CacheDatabase import io.element.android.libraries.di.annotations.ApplicationContext import io.element.encrypteddb.SqlCipherDriverFactory -import io.element.encrypteddb.passphrase.RandomSecretPassphraseProvider +import io.element.encrypteddb.passphrase.RandomDatabaseSecretProvider +import io.element.encrypteddb.utils.ReplaceDatabaseKey +import timber.log.Timber @BindingContainer @ContributesTo(AppScope::class) @@ -35,9 +37,20 @@ object CacheStoreModule { parentDir.mkdirs() } - val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile) + val rekeyMigrationVersion = 2L + val passphraseProvider = RandomDatabaseSecretProvider(context, secretFile) val driver = SqlCipherDriverFactory(passphraseProvider) - .create(CacheDatabase.Schema, "$name.db", context) + .create( + schema = CacheDatabase.Schema, + name = "$name.db", + context = context + ) { db, oldVersion, newVersion -> + Timber.d("Migrating $name database from version $oldVersion to $newVersion") + if (rekeyMigrationVersion in oldVersion..newVersion) { + ReplaceDatabaseKey(passphraseProvider).replaceKey(name, db) + } + } + return CacheDatabase(driver) } } diff --git a/libraries/cachestore/impl/src/main/sqldelight/migrations/1.sqm b/libraries/cachestore/impl/src/main/sqldelight/migrations/1.sqm new file mode 100644 index 0000000000..f038b85649 --- /dev/null +++ b/libraries/cachestore/impl/src/main/sqldelight/migrations/1.sqm @@ -0,0 +1,8 @@ +-- Migrate DB from version 1 +-- Initial schema for CacheData table + +CREATE TABLE IF NOT EXISTS CacheData ( + key TEXT NOT NULL PRIMARY KEY, + value TEXT NOT NULL, + updatedAt INTEGER NOT NULL +); diff --git a/libraries/cachestore/impl/src/main/sqldelight/migrations/2.sqm b/libraries/cachestore/impl/src/main/sqldelight/migrations/2.sqm new file mode 100644 index 0000000000..e7adfb091a --- /dev/null +++ b/libraries/cachestore/impl/src/main/sqldelight/migrations/2.sqm @@ -0,0 +1,2 @@ +-- Migrate DB from version 2 +-- Dummy migration to trigger a re-key diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt index e4ac1a949f..84ab2d2a18 100644 --- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/SqlCipherDriverFactory.kt @@ -9,30 +9,54 @@ package io.element.encrypteddb import android.content.Context +import androidx.sqlite.db.SupportSQLiteDatabase import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import io.element.encrypteddb.passphrase.PassphraseProvider +import io.element.android.libraries.androidutils.crypto.ClientSecret +import io.element.encrypteddb.passphrase.DatabaseSecretProvider import net.zetetic.database.sqlcipher.SupportOpenHelperFactory /** * Creates an encrypted version of the [SqlDriver] using SQLCipher's [SupportOpenHelperFactory]. - * @param passphraseProvider Provides the passphrase needed to use the SQLite database with SQLCipher. + * @param databaseSecretProvider Provides the passphrase needed to use the SQLite database with SQLCipher. */ class SqlCipherDriverFactory( - private val passphraseProvider: PassphraseProvider, + private val databaseSecretProvider: DatabaseSecretProvider, ) { + companion object { + init { + System.loadLibrary("sqlcipher") + } + } + /** * Returns a valid [SqlDriver] with SQLCipher support. * @param schema The SQLite DB schema. * @param name The name of the database to create. * @param context Android [Context], used to instantiate the driver. + * @param onUpgradeCallback Optional callback to handle database upgrades, which will be called in the [AndroidSqliteDriver.Callback.onUpgrade] method. */ - fun create(schema: SqlSchema>, name: String, context: Context): SqlDriver { - System.loadLibrary("sqlcipher") - val passphrase = passphraseProvider.getPassphrase() - val factory = SupportOpenHelperFactory(passphrase) - return AndroidSqliteDriver(schema = schema, context = context, name = name, factory = factory) + fun create( + schema: SqlSchema>, + name: String, + context: Context, + onUpgradeCallback: ((driver: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) -> Unit)? = null, + ): SqlDriver { + val key = when (val secret = databaseSecretProvider.getSecret()) { + // For a raw key, we need the blob representation (`x'...'`) as bytes + is ClientSecret.RawKey -> secret.formattedAsString().toByteArray() + // For a passphrase, we need the bytes for the hex string representation + is ClientSecret.Passphrase -> secret.formattedAsString().hexToByteArray() + } + val factory = SupportOpenHelperFactory(key) + return AndroidSqliteDriver(schema = schema, context = context, name = name, factory = factory, callback = object : AndroidSqliteDriver.Callback( + schema + ) { + override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) { + onUpgradeCallback?.invoke(db, oldVersion, newVersion) + } + }) } } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/DatabaseSecretProvider.kt similarity index 51% rename from libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt rename to libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/DatabaseSecretProvider.kt index a72dc73596..140a5d8de2 100644 --- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/PassphraseProvider.kt +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/DatabaseSecretProvider.kt @@ -8,12 +8,19 @@ package io.element.encrypteddb.passphrase +import io.element.android.libraries.androidutils.crypto.ClientSecret + /** * An abstraction to implement secure providers for SQLCipher passphrases. */ -interface PassphraseProvider { +interface DatabaseSecretProvider { /** - * Returns a passphrase for SQLCipher in [ByteArray] format. + * Returns a secret for SQLCipher. */ - fun getPassphrase(): ByteArray + fun getSecret(): ClientSecret + + /** + * Resets the passphrase, for example by deleting the persisted secret. Returns `true` if the reset was successful, `false` otherwise. + */ + fun reset(): Boolean } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomDatabaseSecretProvider.kt similarity index 56% rename from libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt rename to libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomDatabaseSecretProvider.kt index fe78a4a8da..246a65da07 100644 --- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomDatabaseSecretProvider.kt @@ -9,35 +9,41 @@ package io.element.encrypteddb.passphrase import android.content.Context +import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.encrypteddb.crypto.EncryptedFile import java.io.File import java.security.SecureRandom /** - * Provides a secure passphrase for SQLCipher by generating a random secret and storing it into an [EncryptedFile]. + * Provides a secure secret for SQLCipher by generating a random secret and storing it into an [EncryptedFile]. * @param context Android [Context], used by [EncryptedFile] for cryptographic operations. * @param file Destination file where the key will be stored. - * @param secretSize Length of the generated secret. + * @param secretSizeBytes Length of the generated secret. */ -class RandomSecretPassphraseProvider( +class RandomDatabaseSecretProvider( private val context: Context, private val file: File, - private val secretSize: Int = 256, -) : PassphraseProvider { - override fun getPassphrase(): ByteArray { + private val secretSizeBytes: Int = 32, +) : DatabaseSecretProvider { + override fun getSecret(): ClientSecret { val encryptedFile = EncryptedFile(context, file) - return if (!file.exists()) { + val bytes = if (!file.exists()) { val secret = generateSecret() encryptedFile.openFileOutput().use { it.write(secret) } secret } else { encryptedFile.openFileInput().use { it.readBytes() } } + return ClientSecret.fromRawBytes(bytes) + } + + override fun reset(): Boolean { + return file.delete() } private fun generateSecret(): ByteArray { - val buffer = ByteArray(size = secretSize) - SecureRandom().nextBytes(buffer) - return buffer + // Generate a random secret of the specified size using a secure random generator. + return ByteArray(size = secretSizeBytes) + .also { SecureRandom().nextBytes(it) } } } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/utils/ReplaceDatabaseKey.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/utils/ReplaceDatabaseKey.kt new file mode 100644 index 0000000000..fafb18794b --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/utils/ReplaceDatabaseKey.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.encrypteddb.utils + +import androidx.sqlite.db.SupportSQLiteDatabase +import io.element.encrypteddb.passphrase.RandomDatabaseSecretProvider +import timber.log.Timber + +/** + * A utility class to replace the encryption key of an existing SQLCipher database. + * This is used during database migrations when we want to change the encryption key. + * + * @param databaseSecretProvider The provider for generating new secrets. + */ +class ReplaceDatabaseKey( + private val databaseSecretProvider: RandomDatabaseSecretProvider +) { + fun replaceKey(name: String, database: SupportSQLiteDatabase) { + Timber.d("Re-keying database $name") + // Reset the passphrase provider to generate a new passphrase + databaseSecretProvider.reset() + + // Get the new secret and convert it to the format expected by SQLCipher + val newSecret = databaseSecretProvider.getSecret() + val key = newSecret.formattedAsString() + + // Use the PRAGMA rekey command to change the encryption key of the database + database.query("PRAGMA rekey = \"$key\";").close() + + // Verify that the database can be accessed with the new key by running a simple query + val result = database.query("select count(*) from sqlite_master").use { cursor -> + if (cursor.moveToNext()) cursor.getLong(0) else -1L + } + if (result >= 0) { + Timber.d("Re-keying database $name completed") + } else { + Timber.e("Re-keying database $name didn't work as expected.") + } + } +} 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 9a7452aa5f..4a91462399 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 @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl import dev.zacsweers.metro.Inject +import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.data.ByteUnit import io.element.android.libraries.core.data.megaBytes 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 812a0dd6fe..c44f7ffae7 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 @@ -12,6 +12,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -30,7 +31,6 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync -import io.element.android.libraries.matrix.impl.ClientSecret import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt index 042dae36d9..bad4ecb169 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGenerator.kt @@ -11,7 +11,7 @@ package io.element.android.libraries.matrix.impl.keys import android.util.Base64 import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.androidutils.crypto.ClientSecret import java.security.SecureRandom @ContributesBinding(AppScope::class) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt index 4699f2a720..6dfade9b7c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/keys/SecretGenerator.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.matrix.impl.keys -import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.androidutils.crypto.ClientSecret private const val PASSPHRASE_SIZE = 256 private const val KEY_SIZE = 32 diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt index 1e5b8e037c..3fdfb7a179 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/storage/SqliteStoreBuilder.kt @@ -7,9 +7,9 @@ package io.element.android.libraries.matrix.impl.storage +import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.core.data.ByteUnit import io.element.android.libraries.core.data.megaBytes -import io.element.android.libraries.matrix.impl.ClientSecret import io.element.android.libraries.matrix.impl.paths.SessionPaths import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.SqliteStoreBuilder as SdkSqliteStoreBuilder diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt index 6bd995348a..12c227946a 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/FakeSecretGenerator.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.matrix.impl.auth -import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.androidutils.crypto.ClientSecret import io.element.android.libraries.matrix.impl.keys.SecretGenerator import io.element.android.libraries.matrix.test.A_PASSPHRASE diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt index 5fa2b263a1..4bda511d42 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/keys/DefaultSecretGeneratorTest.kt @@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.impl.keys import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.androidutils.crypto.ClientSecret import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt index c33a67790b..7eeff7db33 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/storage/FakeSqliteStoreBuilder.kt @@ -7,7 +7,7 @@ package io.element.android.libraries.matrix.impl.storage -import io.element.android.libraries.matrix.impl.ClientSecret +import io.element.android.libraries.androidutils.crypto.ClientSecret import org.matrix.rustcomponents.sdk.ClientBuilder class FakeSqliteStoreBuilder : SqliteStoreBuilder { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/history/di/PushHistoryModule.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/history/di/PushHistoryModule.kt index 6472883d4d..93194b2738 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/history/di/PushHistoryModule.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/history/di/PushHistoryModule.kt @@ -17,7 +17,9 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.push.impl.PushDatabase import io.element.encrypteddb.SqlCipherDriverFactory -import io.element.encrypteddb.passphrase.RandomSecretPassphraseProvider +import io.element.encrypteddb.passphrase.RandomDatabaseSecretProvider +import io.element.encrypteddb.utils.ReplaceDatabaseKey +import timber.log.Timber @BindingContainer @ContributesTo(AppScope::class) @@ -36,9 +38,20 @@ object PushHistoryModule { parentDir.mkdirs() } - val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile) + val rekeyMigrationVersion = 2L + val passphraseProvider = RandomDatabaseSecretProvider(context, secretFile) val driver = SqlCipherDriverFactory(passphraseProvider) - .create(PushDatabase.Schema, "$name.db", context) + .create( + schema = PushDatabase.Schema, + name = "$name.db", + context = context + ) { db, oldVersion, newVersion -> + Timber.d("Migrating $name database from version $oldVersion to $newVersion") + if (rekeyMigrationVersion in oldVersion..newVersion) { + ReplaceDatabaseKey(passphraseProvider).replaceKey(name, db) + } + } + return PushDatabase(driver) } } diff --git a/libraries/push/impl/src/main/sqldelight/migrations/2.sqm b/libraries/push/impl/src/main/sqldelight/migrations/2.sqm new file mode 100644 index 0000000000..e7adfb091a --- /dev/null +++ b/libraries/push/impl/src/main/sqldelight/migrations/2.sqm @@ -0,0 +1,2 @@ +-- Migrate DB from version 2 +-- Dummy migration to trigger a re-key diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt index fb2c9a2c78..8955aa4a27 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt @@ -17,7 +17,9 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.sessionstorage.impl.SessionDatabase import io.element.encrypteddb.SqlCipherDriverFactory -import io.element.encrypteddb.passphrase.RandomSecretPassphraseProvider +import io.element.encrypteddb.passphrase.RandomDatabaseSecretProvider +import io.element.encrypteddb.utils.ReplaceDatabaseKey +import timber.log.Timber @BindingContainer @ContributesTo(AppScope::class) @@ -36,9 +38,20 @@ object SessionStorageModule { parentDir.mkdirs() } - val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile) + val rekeyMigrationVersion = 11L + val passphraseProvider = RandomDatabaseSecretProvider(context, secretFile) val driver = SqlCipherDriverFactory(passphraseProvider) - .create(SessionDatabase.Schema, "$name.db", context) + .create( + schema = SessionDatabase.Schema, + name = "$name.db", + context = context, + ) { db, oldVersion, newVersion -> + Timber.d("Migrating $name database from version $oldVersion to $newVersion") + if (rekeyMigrationVersion in oldVersion..newVersion) { + ReplaceDatabaseKey(passphraseProvider).replaceKey(name, db) + } + } + return SessionDatabase(driver) } } diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/11.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/11.sqm new file mode 100644 index 0000000000..3d61c92c07 --- /dev/null +++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/11.sqm @@ -0,0 +1,2 @@ +-- Migrate DB from version 11 +-- Dummy migration to trigger a re-key diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt index a9647ba3a1..8659fc9634 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt @@ -57,7 +57,7 @@ class DefaultAnalyticsColdStartWatcher( override fun onRoomListVisible() { if (isColdStart.getAndSet(false)) { analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart) { - Timber.d("Room list is visible, finishing cold start check. Elapsed: ${it.duration}") + Timber.d("Room list is visible, finishing cold start check after ${it.duration}") } } } From ab4b11b9f06092848e496e075232b4205a6fdaf8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 27 May 2026 17:26:14 +0200 Subject: [PATCH 262/471] Disable cron jobs and private SSH key jobs in forks (#6821) * Try disabling cron jobs in forks * Try fixing quality checks * Try fixing tests and enterprise builds * Stop using the ssh private key in more quality checks * Try disabling codecov if there is no token --- .github/workflows/build_enterprise.yml | 2 +- .github/workflows/generate_github_pages.yml | 2 +- .github/workflows/gradle-wrapper-update.yml | 2 +- .github/workflows/nightlyReports.yml | 3 ++- .github/workflows/quality.yml | 28 ++++++++++----------- .github/workflows/stale-issues.yml | 1 + .github/workflows/sync-localazy.yml | 2 +- .github/workflows/sync-sas-strings.yml | 2 +- .github/workflows/tests.yml | 7 ++++-- 9 files changed, 27 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 956655b262..92cfe6ded5 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -19,7 +19,7 @@ jobs: name: Build Enterprise APKs runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} strategy: matrix: variant: [debug, release, nightly] diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index 8de21b3ef1..6b4d08fd39 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -11,7 +11,7 @@ jobs: generate-github-pages: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} permissions: contents: write steps: diff --git a/.github/workflows/gradle-wrapper-update.yml b/.github/workflows/gradle-wrapper-update.yml index 66078b7b4b..a8986b6005 100644 --- a/.github/workflows/gradle-wrapper-update.yml +++ b/.github/workflows/gradle-wrapper-update.yml @@ -11,7 +11,7 @@ jobs: update-gradle-wrapper: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event_name != 'pull_request' && github.event.repository.fork != true }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 371c11b3ff..7c83d63ebd 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -17,7 +17,7 @@ jobs: nightlyReports: name: Create kover report artifact and upload sonar result. runs-on: ubuntu-latest - if: ${{ github.repository == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be @@ -75,6 +75,7 @@ jobs: dependency-analysis: name: Dependency analysis runs-on: ubuntu-latest + if: ${{ github.event.repository.fork != true }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ceaa86016a..a9180f6276 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -38,11 +38,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Run code quality check suite run: ./tools/check/check_code_quality.sh @@ -101,11 +101,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -142,11 +142,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -176,11 +176,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -221,11 +221,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -262,11 +262,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -303,11 +303,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: Run docs check # This is equivalent to `./gradlew checkDocs`, but we avoid having to install java and gradle diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 1958e80083..f5d2fe911c 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -9,6 +9,7 @@ permissions: {} jobs: stale: runs-on: ubuntu-latest + if: ${{ github.event.repository.fork != true }} permissions: issues: write steps: diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index 6de0f3b0df..131a2f3073 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -11,7 +11,7 @@ jobs: sync-localazy: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/sync-sas-strings.yml b/.github/workflows/sync-sas-strings.yml index 9f7a67cc22..832ef4a71c 100644 --- a/.github/workflows/sync-sas-strings.yml +++ b/.github/workflows/sync-sas-strings.yml @@ -11,7 +11,7 @@ jobs: sync-sas-strings: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} # No concurrency required, runs every time on a schedule. steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 842c8113f4..dc8b2852d2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,11 +56,11 @@ jobs: ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} + if: ${{ github.event.repository.fork != true }} run: git submodule update --init --recursive - name: ☕️ Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -109,6 +109,9 @@ jobs: # https://github.com/codecov/codecov-action - name: ☂️ Upload coverage reports to codecov uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + env: + HAS_TOKEN: ${{ secrets.CODECOV_TOKEN != ''}} + if: ${{ env.HAS_TOKEN == 'true' }} with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} From bf1462f9b1f17583ed4ef1849a99568705c8b755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 15 May 2026 14:57:39 +0200 Subject: [PATCH 263/471] WIP --- build.gradle.kts | 5 +++-- .../home/impl/components/HomeTopBar.kt | 3 ++- .../impl/spacefilters/SpaceFiltersView.kt | 9 +++++---- .../impl/screens/error/ErrorView.kt | 2 +- .../impl/common/ui/LocationPinMarkers.kt | 3 ++- .../location/impl/share/ShareLocationView.kt | 4 +++- .../location/impl/show/ShowLocationView.kt | 3 ++- .../screens/qrcode/error/QrCodeErrorView.kt | 2 +- .../features/messages/impl/MessagesView.kt | 3 ++- .../impl/threads/list/ThreadsListView.kt | 4 +++- .../event/TimelineItemLocationContent.kt | 7 +++++-- .../impl/advanced/AdvancedSettingsView.kt | 4 +++- gradle/libs.versions.toml | 1 + .../appyx/SafeChildrenTransitionScope.kt | 1 + .../theme/components/ModalBottomSheet.kt | 7 +++++-- .../matrix/ui/components/AvatarPickerView.kt | 1 + .../libraries/qrcode/QrCodeCameraView.kt | 2 +- tools/detekt/detekt.yml | 19 ++++++++++++++++++- 18 files changed, 59 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 474b868eda..1aaccbe2f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,8 @@ tasks.register("clean").configure { delete(rootProject.layout.buildDirectory) } -private val ktLintVersion = the().versions.ktlint.get() +private val catalog = the() +private val ktLintVersion = catalog.versions.ktlint.get() allprojects { // Detekt @@ -46,7 +47,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.5.8") + detektPlugins(catalog.detekt.compose.rules) detektPlugins(project(":tests:detekt-rules")) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index 5f55beaf60..a563520e09 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -10,6 +10,7 @@ package io.element.android.features.home.impl.components import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -160,7 +161,7 @@ fun HomeTopBar( } @Composable -private fun RoomListMenuItems( +private fun RowScope.RoomListMenuItems( onToggleSearch: () -> Unit, onMenuActionClick: (RoomListMenuAction) -> Unit, canReportBug: Boolean, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt index 1f0d6ff7d9..8a270f1b85 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt @@ -48,6 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -93,7 +94,7 @@ fun SpaceFiltersView( SpaceFiltersBottomSheetContent( filters = state.visibleFilters, searchQuery = state.searchQuery, - onFilterSelected = { filter -> + onSelectFilter = { filter -> state.eventSink(SpaceFiltersEvent.Selecting.SelectFilter(filter)) } ) @@ -105,9 +106,9 @@ fun SpaceFiltersView( @Composable private fun SpaceFiltersBottomSheetContent( - filters: List, + filters: ImmutableList, searchQuery: TextFieldState, - onFilterSelected: (SpaceServiceFilter) -> Unit, + onSelectFilter: (SpaceServiceFilter) -> Unit, modifier: Modifier = Modifier ) { Column( @@ -133,7 +134,7 @@ private fun SpaceFiltersBottomSheetContent( items(filters) { filter -> SpaceFilterItem( filter = filter, - onClick = { onFilterSelected(filter) } + onClick = { onSelectFilter(filter) } ) } } diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt index 4db2aa9ad5..cc94951dbd 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt @@ -147,7 +147,7 @@ private fun DoneButton( } @Composable -private fun Buttons( +private fun ColumnScope.Buttons( onRetry: () -> Unit, onCancel: () -> Unit, ) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt index 762a719cf4..e30dfd53de 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/LocationPinMarkers.kt @@ -14,6 +14,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.Location import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.rememberLocationPinBitmap +import kotlinx.collections.immutable.ImmutableList import kotlinx.serialization.json.JsonPrimitive import org.maplibre.compose.expressions.dsl.and import org.maplibre.compose.expressions.dsl.asString @@ -66,7 +67,7 @@ data class LocationMarkerData( */ @Composable fun LocationPinMarkers( - markers: List, + markers: ImmutableList, onMarkerClick: ((LocationMarkerData) -> Unit)? = null, onClusterClick: ((Position) -> Unit)? = null, ) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 985c6c8bdc..52400cd49d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -180,6 +181,7 @@ private fun StartLiveLocationActionView( onActionSuccess: () -> Unit, modifier: Modifier = Modifier, ) { + val updatedOnActionSuccess by rememberUpdatedState(onActionSuccess) Box(modifier = modifier) { val asyncIndicatorState = rememberAsyncIndicatorState() AsyncIndicatorHost(state = asyncIndicatorState) @@ -202,7 +204,7 @@ private fun StartLiveLocationActionView( } } is AsyncAction.Success -> { - LaunchedEffect(action) { onActionSuccess() } + LaunchedEffect(action) { updatedOnActionSuccess() } } else -> Unit } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 80ffaabedd..231bc01b12 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.launch import org.maplibre.compose.camera.CameraMoveReason import org.maplibre.compose.camera.CameraPosition @@ -172,7 +173,7 @@ fun ShowLocationView( trackUserLocation = state.isTrackMyLocation ) val markers = remember(state.locationShares) { - state.locationShares.map { it.toMarkerData() } + state.locationShares.map { it.toMarkerData() }.toImmutableList() } LocationPinMarkers(markers) }, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt index a6d9da3de2..6b5da8f300 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorView.kt @@ -125,7 +125,7 @@ private fun Content(errorScreenType: QrCodeErrorScreenType) { } @Composable -private fun Buttons( +private fun ColumnScope.Buttons( onRetry: () -> Unit, onCancel: () -> Unit, ) { 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 3e6f14e805..b299ea42a9 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 @@ -17,6 +17,7 @@ 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.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets @@ -412,7 +413,7 @@ fun MessagesView( } @Composable -internal fun MessagesMenuActions( +internal fun RowScope.MessagesMenuActions( displayThreads: Boolean, roomCallState: RoomCallState, onJoinCallClick: (isAudioCall: Boolean) -> Unit, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt index 5e26d849a5..7d741eb3ea 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/list/ThreadsListView.kt @@ -28,6 +28,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -157,6 +158,7 @@ private fun ScrollHelper( listState: LazyListState, onPaginate: () -> Unit, ) { + val updatedOnPaginate by rememberUpdatedState(onPaginate) val lastVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex + listState.layoutInfo.visibleItemsInfo.size - 1 } } @@ -168,7 +170,7 @@ private fun ScrollHelper( } LaunchedEffect(needsPagination, lastVisibleItemIndex) { if (needsPagination) { - onPaginate() + updatedOnPaginate() delay(400L) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt index 1003d03b4b..12368530a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemLocationContent.kt @@ -9,7 +9,9 @@ package io.element.android.features.messages.impl.timeline.model.event import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import io.element.android.features.location.api.Location import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -103,8 +105,9 @@ private fun rememberIsLiveLocationActive( mode: TimelineItemLocationContent.Mode.Live, currentTimeMillis: () -> Long, ): Boolean { + val updatedCurrentTimeMillis by rememberUpdatedState(currentTimeMillis) fun TimelineItemLocationContent.Mode.Live.isActive(): Boolean { - return isActive && endTimestamp > currentTimeMillis() + return isActive && endTimestamp > updatedCurrentTimeMillis() } return produceState( initialValue = mode.isActive(), @@ -112,7 +115,7 @@ private fun rememberIsLiveLocationActive( key2 = mode.isActive, ) { if (mode.isActive) { - val remainingMillis = mode.endTimestamp - currentTimeMillis() + val remainingMillis = mode.endTimestamp - updatedCurrentTimeMillis() delay(remainingMillis) } value = false diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index af28e3443f..7b5d0c5a9a 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -343,6 +344,7 @@ private fun LiveLocationUpdatesSection( onOpenAppPermissionsClick: () -> Unit, modifier: Modifier = Modifier, ) { + val updatedOnValueSaved by rememberUpdatedState(onValueSaved) PreferenceCategory( modifier = modifier, showTopDivider = true, @@ -382,7 +384,7 @@ private fun LiveLocationUpdatesSection( value = sliderValue.toFloat(), onValueChange = { sliderValue = it.roundToInt() }, onValueChangeFinish = { - onValueSaved(sliderValue) + updatedOnValueSaved(sliderValue) }, valueRange = valueRange, colors = SliderDefaults.colors( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b526c0ab69..524ec54c73 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,6 +72,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref compose_compiler_plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } # https://developer.android.com/studio/write/java8-support#library-desugaring-versions android_desugar = "com.android.tools:desugar_jdk_libs:2.1.5" +detekt_compose_rules = "io.nlopez.compose.rules:detekt:0.4.28" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } metro_gradle_plugin = { module = "dev.zacsweers.metro:gradle-plugin", version.ref = "metro" } kotlin_compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt index 00e49d7bee..b90e95beca 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/appyx/SafeChildrenTransitionScope.kt @@ -186,6 +186,7 @@ class SafeChildrenTransitionScope( } @OptIn(ExperimentalSharedTransitionApi::class) +@Suppress("ContentSlotReused") @Composable inline fun ParentNode.SafeChildren( navModel: NavModel, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt index 689cba727f..ec5f7ce456 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt @@ -24,6 +24,8 @@ import androidx.compose.material3.SheetState import androidx.compose.material3.contentColorFor import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.movableContentOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape @@ -88,14 +90,15 @@ fun ModalBottomSheet( dragHandle = dragHandle, contentWindowInsets = contentWindowInsets, ) { + val movableContent = remember { movableContentOf { content() } } if (scrollable) { Column( modifier = Modifier.verticalScroll(rememberScrollState()), ) { - content() + movableContent() } } else { - content() + movableContent() } } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt index 4d3ba4dc6b..81822a04cc 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarPickerView.kt @@ -184,6 +184,7 @@ private fun PickButton( } } +@Suppress("ModifierClickableOrder") @Composable private fun BoxScope.OverlayEditButton( onClick: () -> Unit, diff --git a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt index 18bd1cff10..77648c0f0b 100644 --- a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt +++ b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt @@ -114,7 +114,7 @@ fun QrCodeCameraView( Box(modifier.clipToBounds()) { if (LocalInspectionMode.current) { Box( - modifier = modifier + modifier = Modifier .background(color = ElementTheme.colors.bgSubtlePrimary), contentAlignment = Alignment.Center, ) { diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 552b46755e..cc99ecab5a 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -150,7 +150,7 @@ exceptions: TooGenericExceptionCaught: active: false SwallowedException: - active: true + active: false ThrowingExceptionsWithoutMessageOrCause: active: true TooGenericExceptionThrown: @@ -224,12 +224,27 @@ comments: Compose: CompositionLocalAllowlist: active: true + # You can optionally define a list of CompositionLocals that are allowed here + allowedCompositionLocals: + - LocalCompoundColors + - LocalSnackbarDispatcher + - LocalCameraPositionState + - LocalMediaItemPresenterFactories + - LocalTimelineItemPresenterFactories + - LocalRoomMemberProfilesCache + - LocalMentionSpanUpdater + - LocalAnalyticsService + - LocalBuildMeta + - LocalUiTestMode + - LocalSdkIntVersionProvider CompositionLocalNaming: active: true ContentEmitterReturningValues: active: true # You can optionally add your own composables here # contentEmitters: MyComposable,MyOtherComposable + ModifierComposable: + active: true ModifierMissing: active: true ModifierReused: @@ -252,6 +267,8 @@ Compose: active: true PreviewPublic: active: true + # You can optionally disable that only previews with @PreviewParameter are flagged + previewPublicOnlyIfParams: false RememberMissing: active: true UnstableCollections: From 97de57fd79658a0ebfefae4598350fa46ad5f536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 15 May 2026 15:04:08 +0200 Subject: [PATCH 264/471] More fixes --- .../preferences/impl/advanced/AdvancedSettingsView.kt | 8 ++++---- .../features/space/impl/addroom/AddRoomToSpaceNode.kt | 2 +- .../features/space/impl/addroom/AddRoomToSpaceView.kt | 9 ++++++--- .../space/impl/addroom/AddRoomToSpaceViewTest.kt | 2 +- .../android/libraries/textcomposer/TextComposer.kt | 8 +++++--- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 7b5d0c5a9a..2f983d30df 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -207,7 +207,7 @@ fun AdvancedSettingsView( if (state.liveLocationMinimumDistanceUpdate != null) { LiveLocationUpdatesSection( value = state.liveLocationMinimumDistanceUpdate, - onValueSaved = { value -> + onSaveValue = { value -> state.eventSink(AdvancedSettingsEvents.SetLiveLocationMinimumDistanceUpdate(value)) }, onOpenAppPermissionsClick = onOpenAppSettingsClick, @@ -340,11 +340,11 @@ private fun ModerationAndSafety( @Composable private fun LiveLocationUpdatesSection( value: Int, - onValueSaved: (Int) -> Unit, + onSaveValue: (Int) -> Unit, onOpenAppPermissionsClick: () -> Unit, modifier: Modifier = Modifier, ) { - val updatedOnValueSaved by rememberUpdatedState(onValueSaved) + val updatedOnSaveValue by rememberUpdatedState(onSaveValue) PreferenceCategory( modifier = modifier, showTopDivider = true, @@ -384,7 +384,7 @@ private fun LiveLocationUpdatesSection( value = sliderValue.toFloat(), onValueChange = { sliderValue = it.roundToInt() }, onValueChangeFinish = { - updatedOnValueSaved(sliderValue) + updatedOnSaveValue(sliderValue) }, valueRange = valueRange, colors = SliderDefaults.colors( diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt index dadd54cf6f..c7aea55488 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceNode.kt @@ -41,7 +41,7 @@ class AddRoomToSpaceNode( AddRoomToSpaceView( state = state, onBackClick = callback::onFinish, - onRoomsAdded = callback::onFinish, + onAddRoom = callback::onFinish, modifier = modifier ) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt index c64efa8074..47c78e6fb8 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceView.kt @@ -23,6 +23,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter @@ -57,9 +59,10 @@ import kotlinx.collections.immutable.toImmutableList fun AddRoomToSpaceView( state: AddRoomToSpaceState, onBackClick: () -> Unit, - onRoomsAdded: () -> Unit, + onAddRoom: () -> Unit, modifier: Modifier = Modifier, ) { + val updatedOnAddRoom by rememberUpdatedState(onAddRoom) fun onRoomToggled(room: SelectRoomInfo) { state.eventSink(AddRoomToSpaceEvent.ToggleRoom(room)) } @@ -78,7 +81,7 @@ fun AddRoomToSpaceView( // Navigate back on success LaunchedEffect(state.saveAction) { if (state.saveAction is AsyncAction.Success) { - onRoomsAdded() + updatedOnAddRoom() } } @@ -247,6 +250,6 @@ internal fun AddRoomToSpaceViewPreview( AddRoomToSpaceView( state = state, onBackClick = {}, - onRoomsAdded = {}, + onAddRoom = {}, ) } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt index 6fc10f1e82..fa47ce63bb 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/addroom/AddRoomToSpaceViewTest.kt @@ -125,7 +125,7 @@ private fun AndroidComposeUiTest.setAddRoomToSpaceView( AddRoomToSpaceView( state = state, onBackClick = onBackClick, - onRoomsAdded = onRoomsAdded, + onAddRoom = onRoomsAdded, ) } } 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 3993b3d64c..0f62eb5234 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 @@ -32,6 +32,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -419,7 +420,7 @@ fun TextComposer( // Re-focus the text input when voice recording ends so the user can continue typing var previousVoiceMessageState by remember { mutableStateOf(voiceMessageState) } - LaunchedEffect(voiceMessageState) { + LaunchedEffect(voiceMessageState, onRequestFocus) { if (voiceMessageState is VoiceMessageState.Idle && previousVoiceMessageState !is VoiceMessageState.Idle) { onRequestFocus() } @@ -522,6 +523,7 @@ private fun StandardLayout( .padding(bottom = 8.dp, top = 8.dp) .weight(1f) ) { + val movableVoiceRecording = remember { movableContentOf { voiceRecording() } } if (voiceMessageState is VoiceMessageState.Idle) { textInput() } else if (composerMode is MessageComposerMode.Special) { @@ -530,10 +532,10 @@ private fun StandardLayout( onResetComposerMode = onResetComposerMode, isTextEmpty = true, ) { - voiceRecording() + movableVoiceRecording() } } else { - voiceRecording() + movableVoiceRecording() } } // To avoid loosing keyboard focus, the IconButton has to be defined here and has to be always enabled. From 5b138e7ad70cb99df7cb2ce65388baefd848955a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 14:38:35 +0200 Subject: [PATCH 265/471] Small cleanup and comment --- build.gradle.kts | 3 +-- gradle/libs.versions.toml | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1aaccbe2f2..661fc9d30a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,6 @@ tasks.register("clean").configure { } private val catalog = the() -private val ktLintVersion = catalog.versions.ktlint.get() allprojects { // Detekt @@ -65,7 +64,7 @@ allprojects { // See https://github.com/JLLeitschuh/ktlint-gradle#configuration configure { - version = ktLintVersion + version = catalog.versions.ktlint.get() android = true ignoreFailures = false enableExperimentalRules = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 524ec54c73..f3e79e3c45 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,6 +61,8 @@ autoservice = "1.1.1" # quality detekt = "1.23.8" +# Stick to version < 0.5 since error detection is broken. See https://mrmans0n.github.io/compose-rules/detekt/#supported-versions-matrix +detekt_compose_rules = "0.4.28" # See https://github.com/pinterest/ktlint/releases/ ktlint = "1.8.0" androidx-test-ext-junit = "1.3.0" @@ -72,7 +74,8 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref compose_compiler_plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } # https://developer.android.com/studio/write/java8-support#library-desugaring-versions android_desugar = "com.android.tools:desugar_jdk_libs:2.1.5" -detekt_compose_rules = "io.nlopez.compose.rules:detekt:0.4.28" +# Used in build.gradle.kts +detekt_compose_rules = { module = "io.nlopez.compose.rules:detekt", version.ref = "detekt_compose_rules" } kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } metro_gradle_plugin = { module = "dev.zacsweers.metro:gradle-plugin", version.ref = "metro" } kotlin_compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } From c409fc2c838b5b8f3c958f302f24bfa7fc085f3c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 22 May 2026 14:48:08 +0200 Subject: [PATCH 266/471] Fix other issues detekted. --- .../android/features/roomdetails/impl/RoomDetailsView.kt | 2 +- .../mediaviewer/impl/viewer/MediaViewerDataSource.kt | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) 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 b30c73b9c8..40210279fa 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 @@ -206,7 +206,7 @@ fun RoomDetailsView( ), trailingContent = ListItemContent.Custom { Box( - modifier = modifier + modifier = Modifier .size(8.dp) .clip(CircleShape) .background(ElementTheme.colors.iconAccentPrimary) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt index b0b81bbde0..6feb3c7d53 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerDataSource.kt @@ -13,8 +13,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.ProduceStateScope import androidx.compose.runtime.State +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.matrix.api.core.EventId @@ -87,8 +89,9 @@ class MediaViewerDataSource( fun produceState( producer: suspend ProduceStateScope>.(StateFlow>) -> Unit ): State> { + val latestProducer by rememberUpdatedState(producer) return produceState(initialValue = initialData()) { - producer(dataFlow) + latestProducer(dataFlow) } } From 7003bf570232655b2be1ceecf9dd2e6adf0648d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 26 May 2026 10:29:50 +0200 Subject: [PATCH 267/471] Fix broken preview --- .../io/element/android/libraries/qrcode/QrCodeCameraView.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt index 77648c0f0b..cae7452cd8 100644 --- a/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt +++ b/libraries/qrcode/src/main/kotlin/io/element/android/libraries/qrcode/QrCodeCameraView.kt @@ -18,6 +18,7 @@ import androidx.camera.view.PreviewView import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -115,6 +116,7 @@ fun QrCodeCameraView( if (LocalInspectionMode.current) { Box( modifier = Modifier + .fillMaxSize() .background(color = ElementTheme.colors.bgSubtlePrimary), contentAlignment = Alignment.Center, ) { From 5ebeddfe2e4ddfe9b946e983907c722dcb42d938 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 26 May 2026 16:16:27 +0200 Subject: [PATCH 268/471] Revert change --- .../features/preferences/impl/advanced/AdvancedSettingsView.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 2f983d30df..41e6ba2d83 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -344,7 +344,6 @@ private fun LiveLocationUpdatesSection( onOpenAppPermissionsClick: () -> Unit, modifier: Modifier = Modifier, ) { - val updatedOnSaveValue by rememberUpdatedState(onSaveValue) PreferenceCategory( modifier = modifier, showTopDivider = true, @@ -384,7 +383,7 @@ private fun LiveLocationUpdatesSection( value = sliderValue.toFloat(), onValueChange = { sliderValue = it.roundToInt() }, onValueChangeFinish = { - updatedOnSaveValue(sliderValue) + onSaveValue(sliderValue) }, valueRange = valueRange, colors = SliderDefaults.colors( From 27982449755f1605b8ba44956291381f2c68ae13 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 27 May 2026 14:34:19 +0200 Subject: [PATCH 269/471] Remove unused import --- .../features/preferences/impl/advanced/AdvancedSettingsView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt index 41e6ba2d83..c108c45d3b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsView.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier From f8712525739fccaf865260586842fbeb811dd1b7 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 28 May 2026 09:56:07 +0200 Subject: [PATCH 270/471] Revert "Disable cron jobs and private SSH key jobs in forks" (#6905) This reverts commit ab4b11b9f06092848e496e075232b4205a6fdaf8. --- .github/workflows/build_enterprise.yml | 2 +- .github/workflows/generate_github_pages.yml | 2 +- .github/workflows/gradle-wrapper-update.yml | 2 +- .github/workflows/nightlyReports.yml | 3 +-- .github/workflows/quality.yml | 28 ++++++++++----------- .github/workflows/stale-issues.yml | 1 - .github/workflows/sync-localazy.yml | 2 +- .github/workflows/sync-sas-strings.yml | 2 +- .github/workflows/tests.yml | 7 ++---- 9 files changed, 22 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 92cfe6ded5..956655b262 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -19,7 +19,7 @@ jobs: name: Build Enterprise APKs runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} strategy: matrix: variant: [debug, release, nightly] diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index 6b4d08fd39..8de21b3ef1 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -11,7 +11,7 @@ jobs: generate-github-pages: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} permissions: contents: write steps: diff --git a/.github/workflows/gradle-wrapper-update.yml b/.github/workflows/gradle-wrapper-update.yml index a8986b6005..66078b7b4b 100644 --- a/.github/workflows/gradle-wrapper-update.yml +++ b/.github/workflows/gradle-wrapper-update.yml @@ -11,7 +11,7 @@ jobs: update-gradle-wrapper: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' && github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 7c83d63ebd..371c11b3ff 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -17,7 +17,7 @@ jobs: nightlyReports: name: Create kover report artifact and upload sonar result. runs-on: ubuntu-latest - if: ${{ github.event.repository.fork != true }} + if: ${{ github.repository == 'element-hq/element-x-android' }} steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be @@ -75,7 +75,6 @@ jobs: dependency-analysis: name: Dependency analysis runs-on: ubuntu-latest - if: ${{ github.event.repository.fork != true }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a9180f6276..ceaa86016a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -38,11 +38,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Run code quality check suite run: ./tools/check/check_code_quality.sh @@ -101,11 +101,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -142,11 +142,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -176,11 +176,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -221,11 +221,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -262,11 +262,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -303,11 +303,11 @@ jobs: persist-credentials: false - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: Run docs check # This is equivalent to `./gradlew checkDocs`, but we avoid having to install java and gradle diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index f5d2fe911c..1958e80083 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -9,7 +9,6 @@ permissions: {} jobs: stale: runs-on: ubuntu-latest - if: ${{ github.event.repository.fork != true }} permissions: issues: write steps: diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index 131a2f3073..6de0f3b0df 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -11,7 +11,7 @@ jobs: sync-localazy: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/sync-sas-strings.yml b/.github/workflows/sync-sas-strings.yml index 832ef4a71c..9f7a67cc22 100644 --- a/.github/workflows/sync-sas-strings.yml +++ b/.github/workflows/sync-sas-strings.yml @@ -11,7 +11,7 @@ jobs: sync-sas-strings: runs-on: ubuntu-latest # Skip in forks - if: ${{ github.event_name != 'pull_request' || github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} # No concurrency required, runs every time on a schedule. steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dc8b2852d2..842c8113f4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,11 +56,11 @@ jobs: ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0 - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules - if: ${{ github.event.repository.fork != true }} + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} run: git submodule update --init --recursive - name: ☕️ Use JDK 21 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 @@ -109,9 +109,6 @@ jobs: # https://github.com/codecov/codecov-action - name: ☂️ Upload coverage reports to codecov uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 - env: - HAS_TOKEN: ${{ secrets.CODECOV_TOKEN != ''}} - if: ${{ env.HAS_TOKEN == 'true' }} with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} From bdedf41f99bb44791d216e1eee251d1a1f046355 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Thu, 28 May 2026 17:52:59 +0300 Subject: [PATCH 271/471] Fix `FLAG_SECURE` splash screen having a pink top bar (#6903) --- app/src/main/res/values-night/themes.xml | 9 ++++++++- app/src/main/res/values/themes.xml | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index af159a0b11..2e610fc0c7 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -11,7 +11,14 @@ @color/splashscreen_bg_dark @drawable/transparent @style/Theme.ElementX + @color/splashscreen_bg_dark + @color/splashscreen_bg_dark + @color/splashscreen_bg_dark - diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 4190fd84f2..26cb0a56bd 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -10,8 +10,14 @@ @color/splashscreen_bg_light @drawable/transparent @style/Theme.ElementX + @color/splashscreen_bg_light + @color/splashscreen_bg_light + @color/splashscreen_bg_light From c88edc9621d342152795b4fcff70545363f91417 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 May 2026 16:54:12 +0200 Subject: [PATCH 272/471] Notification settings: pick custom sounds for messages and call ringtones (#6897) * Notification settings: pick custom sounds for messages and call ringtones (#6702) * Feature: add notification sound settings for messages and calls * Add sound not available banner, accessibility checks * Fix build and Konsist convention violations - libraries:preferences:api now uses io.element.android-compose-library so NotificationSound's @Immutable annotation can resolve, matching the pattern in libraries:matrix:api. - Rename SoundUnavailableBannerStateProvider to NotificationSoundUnavailableStateProvider so the class name contains the parameter type as required by KonsistClassNameTest. * Suppress LargeClass on RoomListPresenterTest The three new sound-unavailable tests pushed the class past Detekt's default LargeClass threshold. Matches the suppression pattern already used on five presenter test classes under features/messages/impl/test. * NotificationSettingsView: guard ringtone-picker launchers in inspection mode rememberLauncherForActivityResult requires a LocalActivityResultRegistryOwner, which Paparazzi-driven preview snapshot tests don't provide, causing NotificationSettingsViewPreview to throw IllegalStateException for every preview parameter. Extract the click handlers into a helper that returns a no-op when LocalInspectionMode.current is true; the runtime path is unchanged. * Handle unresolved notification sounds mid session * Quality checks, refactor mid session error ui * Suppress detekt LargeClass on NotificationSettingsPresenterTest * Remove notification sound resilience layer, replace with file copy * Reselect element sound option, code cleanup * Add element fade notification sound option * Fix new file headers * Revert to flowOf(Unit) for announcement service * Add ringtone picker dialog to match message selector * Update strings in conjunction with ios * Update test to reflect new string * Sync strings. * Update screenshots * Fix test * Remove not properly located translations. This will be fixed in the next String sync. * Fix test on CI --------- Co-authored-by: Jenna Vassar Co-authored-by: ElementBot --- app/src/main/res/xml/file_providers.xml | 1 + .../impl/store/DefaultAnnouncementStore.kt | 5 +- .../impl/components/RoomListContentView.kt | 28 +- .../impl/roomlist/RoomListPresenterTest.kt | 1 + .../NotificationSettingsEvents.kt | 14 + .../NotificationSettingsPresenter.kt | 249 ++++++ .../NotificationSettingsState.kt | 29 + .../NotificationSettingsStateProvider.kt | 67 ++ .../notifications/NotificationSettingsView.kt | 201 +++++ .../notifications/NotificationSoundPicker.kt | 55 ++ .../impl/src/main/res/values/localazy.xml | 9 + .../NotificationSettingsPresenterTest.kt | 765 ++++++++++++++++++ .../NotificationSettingsViewTest.kt | 42 +- .../NotificationSoundPickerTest.kt | 90 +++ .../NotificationSoundStorageTest.kt | 80 ++ libraries/preferences/api/build.gradle.kts | 2 +- .../api/store/AppPreferencesStore.kt | 22 + .../api/store/NotificationSound.kt | 77 ++ .../impl/store/DefaultAppPreferencesStore.kt | 79 ++ .../test/InMemoryAppPreferencesStore.kt | 58 +- libraries/push/api/build.gradle.kts | 1 + .../notifications/NotificationSoundUpdater.kt | 26 + .../notifications/SoundDisplayNameResolver.kt | 18 + .../sound/NotificationSoundCopier.kt | 47 ++ libraries/push/impl/build.gradle.kts | 2 + .../DefaultSoundDisplayNameResolver.kt | 35 + .../NotificationSoundFileProviderAuthority.kt | 18 + .../channels/NotificationChannels.kt | 293 +++++-- .../sound/DefaultNotificationSoundCopier.kt | 208 +++++ .../impl/src/main/res/raw/element_fade.mp3 | Bin 0 -> 33645 bytes .../channels/FakeNotificationChannels.kt | 22 +- .../channels/NotificationChannelsTest.kt | 415 +++++++++- .../DefaultNotificationCreatorTest.kt | 4 +- .../DefaultNotificationSoundCopierTest.kt | 82 ++ libraries/push/test/build.gradle.kts | 1 + .../FakeNotificationSoundUpdater.kt | 31 + .../FakeSoundDisplayNameResolver.kt | 19 + .../sound/FakeNotificationSoundCopier.kt | 24 + .../src/main/res/values-fi/translations.xml | 4 - .../src/main/res/values-fr/translations.xml | 4 - .../src/main/res/values-ja/translations.xml | 5 - .../src/main/res/values-pl/translations.xml | 7 - .../src/main/res/values-zh/translations.xml | 5 - .../src/main/res/values/localazy.xml | 6 - ...ions_NotificationSettingsView_Day_0_en.png | 4 +- ...ons_NotificationSettingsView_Day_14_en.png | 3 + ...ons_NotificationSettingsView_Day_15_en.png | 3 + ...ons_NotificationSettingsView_Day_16_en.png | 3 + ...ons_NotificationSettingsView_Day_17_en.png | 3 + ...ons_NotificationSettingsView_Day_18_en.png | 3 + ...ons_NotificationSettingsView_Day_19_en.png | 3 + ...ions_NotificationSettingsView_Day_1_en.png | 4 +- ...ons_NotificationSettingsView_Day_20_en.png | 3 + ...ions_NotificationSettingsView_Day_2_en.png | 4 +- ...ions_NotificationSettingsView_Day_3_en.png | 4 +- ...ions_NotificationSettingsView_Day_4_en.png | 4 +- ...ions_NotificationSettingsView_Day_5_en.png | 4 +- ...ions_NotificationSettingsView_Day_6_en.png | 4 +- ...ions_NotificationSettingsView_Day_7_en.png | 4 +- ...ions_NotificationSettingsView_Day_8_en.png | 4 +- ...ions_NotificationSettingsView_Day_9_en.png | 4 +- ...ns_NotificationSettingsView_Night_0_en.png | 4 +- ...s_NotificationSettingsView_Night_14_en.png | 3 + ...s_NotificationSettingsView_Night_15_en.png | 3 + ...s_NotificationSettingsView_Night_16_en.png | 3 + ...s_NotificationSettingsView_Night_17_en.png | 3 + ...s_NotificationSettingsView_Night_18_en.png | 3 + ...s_NotificationSettingsView_Night_19_en.png | 3 + ...ns_NotificationSettingsView_Night_1_en.png | 4 +- ...s_NotificationSettingsView_Night_20_en.png | 3 + ...ns_NotificationSettingsView_Night_2_en.png | 4 +- ...ns_NotificationSettingsView_Night_3_en.png | 4 +- ...ns_NotificationSettingsView_Night_4_en.png | 4 +- ...ns_NotificationSettingsView_Night_5_en.png | 4 +- ...ns_NotificationSettingsView_Night_6_en.png | 4 +- ...ns_NotificationSettingsView_Night_7_en.png | 4 +- ...ns_NotificationSettingsView_Night_8_en.png | 4 +- ...ns_NotificationSettingsView_Night_9_en.png | 4 +- tools/localazy/config.json | 1 + 79 files changed, 3135 insertions(+), 139 deletions(-) create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPicker.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPickerTest.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundStorageTest.kt create mode 100644 libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/NotificationSound.kt create mode 100644 libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt create mode 100644 libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/SoundDisplayNameResolver.kt create mode 100644 libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSoundDisplayNameResolver.kt create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationSoundFileProviderAuthority.kt create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopier.kt create mode 100644 libraries/push/impl/src/main/res/raw/element_fade.mp3 create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopierTest.kt create mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationSoundUpdater.kt create mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeSoundDisplayNameResolver.kt create mode 100644 libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/sound/FakeNotificationSoundCopier.kt create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_14_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_15_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_16_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_17_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_18_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_19_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_20_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_14_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_15_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_16_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_17_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_18_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_19_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_20_en.png diff --git a/app/src/main/res/xml/file_providers.xml b/app/src/main/res/xml/file_providers.xml index ac74590cba..330a2e0c3f 100644 --- a/app/src/main/res/xml/file_providers.xml +++ b/app/src/main/res/xml/file_providers.xml @@ -7,4 +7,5 @@ --> + diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt index 5a6c135247..2093baa8b3 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt @@ -35,8 +35,9 @@ class DefaultAnnouncementStore( override fun announcementStatusFlow(announcement: Announcement): Flow { val key = announcement.toKey() - // Announcement.Fullscreen.Space is disabled, consider it's shown - // For NewNotificationSound, a migration will set it to Show on application upgrade (see AppMigration08) + // Default-status convention: Shown for one-shot intro/feature announcements that should + // NOT replay on first launch (Fullscreen.Space is disabled; NewNotificationSound is opted + // in by AppMigration08). val defaultStatus = when (announcement) { Announcement.Fullscreen.Space -> AnnouncementStatus.Shown Announcement.NewNotificationSound -> AnnouncementStatus.Shown diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt index a03399baf7..dee175a7b1 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt @@ -245,19 +245,25 @@ private fun RoomsViewList( ) } } - SecurityBannerState.None -> if (state.fullScreenIntentPermissionsState.shouldDisplayBanner) { - item { - FullScreenIntentPermissionBanner(state = state.fullScreenIntentPermissionsState) + // Banner precedence (top-to-bottom): full-screen-intent > battery-optimization > + // new-notification-sound > sound-unavailable. At most one renders at a time. + SecurityBannerState.None -> when { + state.fullScreenIntentPermissionsState.shouldDisplayBanner -> { + item { + FullScreenIntentPermissionBanner(state = state.fullScreenIntentPermissionsState) + } } - } else if (state.batteryOptimizationState.shouldDisplayBanner) { - item { - BatteryOptimizationBanner(state = state.batteryOptimizationState) + state.batteryOptimizationState.shouldDisplayBanner -> { + item { + BatteryOptimizationBanner(state = state.batteryOptimizationState) + } } - } else if (state.showNewNotificationSoundBanner) { - item { - NewNotificationSoundBanner( - onDismissClick = { eventSink(RoomListEvent.DismissNewNotificationSoundBanner) }, - ) + state.showNewNotificationSoundBanner -> { + item { + NewNotificationSoundBanner( + onDismissClick = { eventSink(RoomListEvent.DismissNewNotificationSoundBanner) }, + ) + } } } } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt index 99a5550a08..5c2c3e294b 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt @@ -89,6 +89,7 @@ import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.seconds +@Suppress("LargeClass") class RoomListPresenterTest { @get:Rule val warmUpRule = WarmUpRule() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt index 8f1b332c8c..5ca7fc9fdf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsEvents.kt @@ -8,6 +8,8 @@ package io.element.android.features.preferences.impl.notifications +import io.element.android.libraries.preferences.api.store.NotificationSound + sealed interface NotificationSettingsEvents { data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents @@ -20,4 +22,16 @@ sealed interface NotificationSettingsEvents { data object ChangePushProvider : NotificationSettingsEvents data object CancelChangePushProvider : NotificationSettingsEvents data class SetPushProvider(val index: Int) : NotificationSettingsEvents + data class SetMessageSound(val sound: NotificationSound) : NotificationSettingsEvents + data class SetCallRingtone(val sound: NotificationSound) : NotificationSettingsEvents + data object DismissMessageSoundCopyError : NotificationSettingsEvents + data object DismissCallRingtoneCopyError : NotificationSettingsEvents + data object ShowMessageSoundDialog : NotificationSettingsEvents + data object DismissMessageSoundDialog : NotificationSettingsEvents + data class SelectMessageSoundPreset(val sound: NotificationSound) : NotificationSettingsEvents + data object LaunchMessageSoundPicker : NotificationSettingsEvents + data object ShowCallRingtoneDialog : NotificationSettingsEvents + data object DismissCallRingtoneDialog : NotificationSettingsEvents + data class SelectCallRingtonePreset(val sound: NotificationSound) : NotificationSettingsEvents + data object LaunchCallRingtonePicker : NotificationSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt index 12f13e734b..03fe2cf31b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenter.kt @@ -16,10 +16,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject +import io.element.android.features.preferences.impl.R import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -30,18 +32,30 @@ import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermiss import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.push.api.PushService +import io.element.android.libraries.push.api.notifications.NotificationSoundUpdater +import io.element.android.libraries.push.api.notifications.SoundDisplayNameResolver +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.CopyResult +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.SoundSlot import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushstore.api.UserPushStore import io.element.android.libraries.pushstore.api.UserPushStoreFactory +import io.element.android.services.toolbox.api.strings.StringProvider import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import timber.log.Timber import kotlin.time.Duration.Companion.seconds @Inject @@ -52,9 +66,22 @@ class NotificationSettingsPresenter( private val pushService: PushService, private val systemNotificationsEnabledProvider: SystemNotificationsEnabledProvider, private val fullScreenIntentPermissionsPresenter: Presenter, + private val appPreferencesStore: AppPreferencesStore, + private val notificationSoundUpdater: NotificationSoundUpdater, + private val notificationSoundCopier: NotificationSoundCopier, + private val soundDisplayNameResolver: SoundDisplayNameResolver, + private val stringProvider: StringProvider, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, ) : Presenter { + // Serializes the pick → copy → persist → recreate pipeline per slot. The copier already locks + // its `.tmp` file, but the persist+recreate window outside the copier is unprotected; + // without this, two rapid picks (e.g. an automation or accessibility service firing back-to- + // back events) could interleave and leave the channel id pointing at the older version while + // DataStore reflects the newer one. + private val messageSoundLock = Mutex() + private val callRingtoneLock = Mutex() + @Composable override fun present(): NotificationSettingsState { val userPushStore = remember { userPushStoreFactory.getOrCreate(matrixClient.sessionId) } @@ -109,6 +136,42 @@ class NotificationSettingsPresenter( var showChangePushProviderDialog by remember { mutableStateOf(false) } + val messageSound by remember { appPreferencesStore.getMessageSoundFlow() }.collectAsState(initial = NotificationSound.SystemDefault) + val callRingtone by remember { appPreferencesStore.getCallRingtoneFlow() }.collectAsState(initial = NotificationSound.SystemDefault) + val persistedMessageSoundTitle by remember { appPreferencesStore.getMessageSoundDisplayNameFlow() }.collectAsState(initial = null) + val persistedCallRingtoneTitle by remember { appPreferencesStore.getCallRingtoneDisplayNameFlow() }.collectAsState(initial = null) + val defaultLabel = stringProvider.getString(R.string.screen_notification_settings_sound_system_default) + + // One-shot classification for the call-ringtone slot only: users who customised the + // channel via Android system settings before the in-app picker existed (channel version 0, + // persisted SystemDefault) get reflected here. The message-sound equivalent is handled by + // the eager migration in DefaultNotificationChannels.createNotificationChannels(). Cleared + // on any in-app pick so a stale read can't bleed into the row after the recreate. + var legacyCallRingtone by remember { mutableStateOf(null) } + LaunchedEffect(Unit) { + val config = appPreferencesStore.getNotificationSoundChannelConfig() + if (config.callRingtone == NotificationSound.SystemDefault && config.callRingtoneVersion == 0) { + legacyCallRingtone = notificationSoundUpdater.readRingingCallChannelSound() + } + } + + val effectiveMessageSound = messageSound + val effectiveCallRingtone = if (callRingtone == NotificationSound.SystemDefault) { + legacyCallRingtone ?: callRingtone + } else { + callRingtone + } + + val messageSoundDisplayName = probeSoundDisplayName(effectiveMessageSound, persistedMessageSoundTitle, defaultLabel) + val callRingtoneDisplayName = probeSoundDisplayName(effectiveCallRingtone, persistedCallRingtoneTitle, defaultLabel) + + var messageSoundCopyError by remember { mutableStateOf(false) } + var callRingtoneCopyError by remember { mutableStateOf(false) } + var showMessageSoundDialog by remember { mutableStateOf(false) } + var pendingMessageSoundPickerLaunch by remember { mutableIntStateOf(0) } + var showCallRingtoneDialog by remember { mutableStateOf(false) } + var pendingCallRingtonePickerLaunch by remember { mutableIntStateOf(0) } + fun CoroutineScope.changePushProvider( data: Pair? ) = launch { @@ -157,6 +220,62 @@ class NotificationSettingsPresenter( NotificationSettingsEvents.ChangePushProvider -> showChangePushProviderDialog = true NotificationSettingsEvents.CancelChangePushProvider -> showChangePushProviderDialog = false is NotificationSettingsEvents.SetPushProvider -> localCoroutineScope.changePushProvider(distributors.getOrNull(event.index)) + is NotificationSettingsEvents.SetMessageSound -> applyMessageSound( + sound = event.sound, + sessionCoroutineScope = sessionCoroutineScope, + onCopyError = { messageSoundCopyError = true }, + onCopySuccess = { messageSoundCopyError = false }, + onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, + ) + is NotificationSettingsEvents.SelectMessageSoundPreset -> { + showMessageSoundDialog = false + applyMessageSound( + sound = event.sound, + sessionCoroutineScope = sessionCoroutineScope, + onCopyError = { messageSoundCopyError = true }, + onCopySuccess = { messageSoundCopyError = false }, + onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, + ) + } + NotificationSettingsEvents.ShowMessageSoundDialog -> showMessageSoundDialog = true + NotificationSettingsEvents.DismissMessageSoundDialog -> showMessageSoundDialog = false + NotificationSettingsEvents.LaunchMessageSoundPicker -> { + showMessageSoundDialog = false + pendingMessageSoundPickerLaunch++ + } + is NotificationSettingsEvents.SetCallRingtone -> { + legacyCallRingtone = null + applyCallRingtone( + sound = event.sound, + sessionCoroutineScope = sessionCoroutineScope, + onCopyError = { callRingtoneCopyError = true }, + onCopySuccess = { callRingtoneCopyError = false }, + onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, + ) + } + is NotificationSettingsEvents.SelectCallRingtonePreset -> { + showCallRingtoneDialog = false + legacyCallRingtone = null + applyCallRingtone( + sound = event.sound, + sessionCoroutineScope = sessionCoroutineScope, + onCopyError = { callRingtoneCopyError = true }, + onCopySuccess = { callRingtoneCopyError = false }, + onChannelFailure = { failure -> changeNotificationSettingAction.value = AsyncAction.Failure(failure) }, + ) + } + NotificationSettingsEvents.ShowCallRingtoneDialog -> showCallRingtoneDialog = true + NotificationSettingsEvents.DismissCallRingtoneDialog -> showCallRingtoneDialog = false + NotificationSettingsEvents.LaunchCallRingtonePicker -> { + showCallRingtoneDialog = false + pendingCallRingtonePickerLaunch++ + } + NotificationSettingsEvents.DismissMessageSoundCopyError -> { + messageSoundCopyError = false + } + NotificationSettingsEvents.DismissCallRingtoneCopyError -> { + callRingtoneCopyError = false + } } } @@ -171,10 +290,140 @@ class NotificationSettingsPresenter( availablePushDistributors = availableDistributors, showChangePushProviderDialog = showChangePushProviderDialog, fullScreenIntentPermissionsState = key(refreshFullScreenIntentSettings) { fullScreenIntentPermissionsPresenter.present() }, + messageSound = NotificationSettingsState.SoundChannelUiState( + sound = effectiveMessageSound, + displayName = messageSoundDisplayName, + copyError = messageSoundCopyError, + ), + callRingtone = NotificationSettingsState.SoundChannelUiState( + sound = effectiveCallRingtone, + displayName = callRingtoneDisplayName, + copyError = callRingtoneCopyError, + ), + showMessageSoundDialog = showMessageSoundDialog, + showCallRingtoneDialog = showCallRingtoneDialog, + pendingMessageSoundPickerLaunch = pendingMessageSoundPickerLaunch, + pendingCallRingtonePickerLaunch = pendingCallRingtonePickerLaunch, eventSink = ::handleEvent, ) } + private fun applyMessageSound( + sound: NotificationSound, + sessionCoroutineScope: CoroutineScope, + onCopyError: () -> Unit, + onCopySuccess: () -> Unit, + onChannelFailure: (Throwable) -> Unit, + ) { + sessionCoroutineScope.launch { + messageSoundLock.withLock { + // Re-selecting the current non-Custom sound is a no-op: skip the channel churn + // (delete + create round-trips through system_server) and the version bump. Custom + // still falls through so the copier refreshes the file from the (possibly updated) + // source URI. + if (sound !is NotificationSound.Custom && appPreferencesStore.getMessageSoundFlow().first() == sound) { + onCopySuccess() + return@withLock + } + val resolved = resolvePickedSound(sound, SoundSlot.Message, onCopyError) ?: return@withLock + runCatchingExceptions { + onCopySuccess() + val newVersion = appPreferencesStore.setMessageSoundAndIncrementVersion(resolved.first, resolved.second) + notificationSoundUpdater.recreateNoisyChannel(resolved.first, newVersion) + // Non-Custom picks bypass the copier, so they don't sweep the prior Custom file + // inline. Drop it now that the new channel no longer references it. + if (resolved.first !is NotificationSound.Custom) { + notificationSoundCopier.deleteStoredSoundFor(SoundSlot.Message) + } + }.onFailure(onChannelFailure) + } + } + } + + private fun applyCallRingtone( + sound: NotificationSound, + sessionCoroutineScope: CoroutineScope, + onCopyError: () -> Unit, + onCopySuccess: () -> Unit, + onChannelFailure: (Throwable) -> Unit, + ) { + sessionCoroutineScope.launch { + callRingtoneLock.withLock { + if (sound !is NotificationSound.Custom && appPreferencesStore.getCallRingtoneFlow().first() == sound) { + onCopySuccess() + return@withLock + } + val resolved = resolvePickedSound(sound, SoundSlot.Call, onCopyError) ?: return@withLock + runCatchingExceptions { + onCopySuccess() + val newVersion = appPreferencesStore.setCallRingtoneAndIncrementVersion(resolved.first, resolved.second) + notificationSoundUpdater.recreateRingingCallChannel(resolved.first, newVersion) + if (resolved.first !is NotificationSound.Custom) { + notificationSoundCopier.deleteStoredSoundFor(SoundSlot.Call) + } + }.onFailure(onChannelFailure) + } + } + } + + /** + * Custom rows prefer [persistedTitle] (captured at copy time), fall back to a live resolver + * probe for legacy data, then to a localised "Custom" — never to [defaultLabel], so an + * unresolvable Custom isn't mislabelled as Default. + */ + @Composable + private fun probeSoundDisplayName( + sound: NotificationSound, + persistedTitle: String?, + defaultLabel: String, + ): String = when (sound) { + NotificationSound.SystemDefault -> defaultLabel + NotificationSound.ElementDefault -> stringProvider.getString(R.string.screen_notification_settings_sound_element_default) + NotificationSound.ElementFade -> stringProvider.getString(R.string.screen_notification_settings_sound_element_fade) + NotificationSound.Silent -> stringProvider.getString(R.string.screen_notification_settings_sound_silent) + is NotificationSound.Custom -> { + val nonBlankPersisted = persistedTitle?.takeUnless { it.isBlank() } + if (nonBlankPersisted != null) { + nonBlankPersisted + } else { + val customFallback = stringProvider.getString(R.string.screen_notification_settings_sound_custom_fallback) + val resolved by produceState(initialValue = "", sound.uri) { + val title = soundDisplayNameResolver.resolveCustomSoundTitle(sound.uri) + value = title?.takeUnless { it.isBlank() } ?: customFallback + } + resolved + } + } + } + + /** + * For a Custom pick, copies into app-private storage and returns (FileProvider URI, title). + * Returns null on copy failure after invoking [onCopyError]. Pass-through for SystemDefault / + * Silent. + */ + private suspend fun resolvePickedSound( + requested: NotificationSound, + slot: SoundSlot, + onCopyError: () -> Unit, + ): Pair? { + if (requested !is NotificationSound.Custom) return requested to null + return when (val result = notificationSoundCopier.copyToAppFiles(requested.uri, slot)) { + is CopyResult.Success -> NotificationSound.Custom(result.fileProviderUriString) to result.displayName + is CopyResult.Failure -> { + Timber.w(result.cause, "Notification sound copy failed for slot=%s", slot) + onCopyError() + null + } + CopyResult.UnplayableSource, + CopyResult.UnplayableCopy, + CopyResult.FileTooLarge -> { + Timber.w("Notification sound rejected: result=%s slot=%s", result::class.simpleName, slot) + onCopyError() + null + } + } + } + @OptIn(FlowPreview::class) private fun CoroutineScope.observeNotificationSettings( target: MutableState, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt index 0a55909e2c..3c59f6c355 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsState.kt @@ -12,6 +12,7 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.pushproviders.api.Distributor import kotlinx.collections.immutable.ImmutableList @@ -23,6 +24,24 @@ data class NotificationSettingsState( val availablePushDistributors: ImmutableList, val showChangePushProviderDialog: Boolean, val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState, + val messageSound: SoundChannelUiState, + val callRingtone: SoundChannelUiState, + val showMessageSoundDialog: Boolean, + val showCallRingtoneDialog: Boolean, + /** + * One-shot trigger for launching the system ringtone picker. Each + * [NotificationSettingsEvents.LaunchMessageSoundPicker] increments the value; + * the view watches this in `LaunchedEffect` and calls the launcher whenever it goes above 0. + * **Always start at 0** in initial state, providers, and previews — a non-zero seed will + * auto-open the picker on screen entry. + */ + val pendingMessageSoundPickerLaunch: Int, + /** + * One-shot trigger for launching the system ringtone picker from the call ringtone dialog. + * Same contract as [pendingMessageSoundPickerLaunch]: **always start at 0**, or the picker + * auto-opens on screen entry. + */ + val pendingCallRingtonePickerLaunch: Int, val eventSink: (NotificationSettingsEvents) -> Unit, ) { sealed interface MatrixSettings { @@ -45,6 +64,16 @@ data class NotificationSettingsState( val appNotificationsEnabled: Boolean, ) + /** + * UI state for one of the two sound rows (message sound, call ringtone). [copyError] is set + * when the last pick failed to copy into app-private storage; the persisted choice stays put. + */ + data class SoundChannelUiState( + val sound: NotificationSound, + val displayName: String, + val copyError: Boolean, + ) + /** * Whether the advanced settings should be shown. * This is true if the current push distributor is in a failure state or if there are multiple push distributors available. diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt index fb396443c3..905f869dbe 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsStateProvider.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.pushproviders.api.Distributor import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -45,6 +46,34 @@ open class NotificationSettingsStateProvider : PreviewParameterProvider Unit = {}, ) = NotificationSettingsState( matrixSettings = NotificationSettingsState.MatrixSettings.Valid( @@ -80,6 +119,20 @@ fun aValidNotificationSettingsState( availablePushDistributors = availablePushDistributors.toImmutableList(), showChangePushProviderDialog = showChangePushProviderDialog, fullScreenIntentPermissionsState = fullScreenIntentPermissionsState, + messageSound = NotificationSettingsState.SoundChannelUiState( + sound = messageSound, + displayName = messageSoundDisplayName, + copyError = messageSoundCopyError, + ), + callRingtone = NotificationSettingsState.SoundChannelUiState( + sound = callRingtone, + displayName = callRingtoneDisplayName, + copyError = callRingtoneCopyError, + ), + showMessageSoundDialog = showMessageSoundDialog, + showCallRingtoneDialog = showCallRingtoneDialog, + pendingMessageSoundPickerLaunch = pendingMessageSoundPickerLaunch, + pendingCallRingtonePickerLaunch = pendingCallRingtonePickerLaunch, eventSink = eventSink, ) @@ -99,6 +152,20 @@ fun aInvalidNotificationSettingsState( availablePushDistributors = persistentListOf(), showChangePushProviderDialog = false, fullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(), + messageSound = NotificationSettingsState.SoundChannelUiState( + sound = NotificationSound.SystemDefault, + displayName = "System default", + copyError = false, + ), + callRingtone = NotificationSettingsState.SoundChannelUiState( + sound = NotificationSound.SystemDefault, + displayName = "System default", + copyError = false, + ), + showMessageSoundDialog = false, + showCallRingtoneDialog = false, + pendingMessageSoundPickerLaunch = 0, + pendingCallRingtonePickerLaunch = 0, eventSink = eventSink, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 09f54b7027..5652276c23 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -8,16 +8,29 @@ package io.element.android.features.preferences.impl.notifications +import android.app.Activity +import android.media.RingtoneManager +import android.net.Uri +import android.provider.Settings +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.progressSemantics import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle +import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.preferences.impl.R import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent @@ -35,13 +48,17 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvents import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.preferences.api.store.NotificationSound import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList /** @@ -182,6 +199,7 @@ private fun NotificationSettingsContentView( onCheckedChange = onMentionNotificationsChange ) } + SoundsPreferenceCategory(state = state) PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_additional_settings_section_title)) { // TODO We are removing the call notification toggle until support for call notifications has been added // PreferenceSwitch( @@ -261,6 +279,189 @@ private fun NotificationSettingsContentView( } } +@Composable +private fun SoundsPreferenceCategory(state: NotificationSettingsState) { + PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_sound_section_title)) { + val launchMessageSoundPicker = rememberSoundPickerOnClick( + type = RingtoneManager.TYPE_NOTIFICATION, + current = state.messageSound.sound, + defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, + onSoundPicked = { sound -> state.eventSink(NotificationSettingsEvents.SetMessageSound(sound)) }, + ) + // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only + // increments fired by LaunchMessageSoundPicker should launch it. + LaunchedEffect(state.pendingMessageSoundPickerLaunch) { + if (state.pendingMessageSoundPickerLaunch > 0) { + launchMessageSoundPicker() + } + } + ListItem( + headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_message_sound_label)) }, + supportingContent = { Text(state.messageSound.displayName) }, + onClick = { state.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) }, + ) + if (state.messageSound.copyError) { + SoundCopyErrorRow( + onDismissClick = { state.eventSink(NotificationSettingsEvents.DismissMessageSoundCopyError) }, + ) + } + if (state.showMessageSoundDialog) { + MessageSoundDialog(state) + } + + val launchCallRingtonePicker = rememberSoundPickerOnClick( + type = RingtoneManager.TYPE_RINGTONE, + current = state.callRingtone.sound, + defaultUri = Settings.System.DEFAULT_RINGTONE_URI, + onSoundPicked = { sound -> state.eventSink(NotificationSettingsEvents.SetCallRingtone(sound)) }, + ) + // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only + // increments fired by LaunchCallRingtonePicker should launch it. + LaunchedEffect(state.pendingCallRingtonePickerLaunch) { + if (state.pendingCallRingtonePickerLaunch > 0) { + launchCallRingtonePicker() + } + } + ListItem( + headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_call_ringtone_label)) }, + supportingContent = { Text(state.callRingtone.displayName) }, + onClick = { state.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) }, + ) + if (state.callRingtone.copyError) { + SoundCopyErrorRow( + onDismissClick = { state.eventSink(NotificationSettingsEvents.DismissCallRingtoneCopyError) }, + ) + } + if (state.showCallRingtoneDialog) { + CallRingtoneDialog(state) + } + } +} + +@Composable +private fun MessageSoundDialog(state: NotificationSettingsState) { + val initialSelection = when (state.messageSound.sound) { + NotificationSound.ElementDefault -> 0 + NotificationSound.ElementFade -> 1 + NotificationSound.SystemDefault -> 2 + else -> null + } + // When the user is on Custom or Silent, no preset row is selected — surface the current + // sound as a subtitle so screen readers (and sighted users) still have state context. + val subtitle = if (initialSelection == null) { + stringResource( + id = R.string.screen_notification_settings_message_sound_dialog_current_subtitle, + state.messageSound.displayName, + ) + } else { + null + } + SingleSelectionDialog( + title = stringResource(id = R.string.screen_notification_settings_message_sound_dialog_title), + subtitle = subtitle, + options = persistentListOf( + ListOption(title = stringResource(id = R.string.screen_notification_settings_sound_element_default)), + ListOption(title = stringResource(id = R.string.screen_notification_settings_sound_element_fade)), + ListOption(title = stringResource(id = R.string.screen_notification_settings_sound_system_default)), + ListOption(title = stringResource(id = R.string.screen_notification_settings_message_sound_dialog_choose_other)), + ), + initialSelection = initialSelection, + onSelectOption = { index -> + when (index) { + 0 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + 1 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementFade)) + 2 -> state.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.SystemDefault)) + else -> state.eventSink(NotificationSettingsEvents.LaunchMessageSoundPicker) + } + }, + onDismissRequest = { state.eventSink(NotificationSettingsEvents.DismissMessageSoundDialog) }, + ) +} + +@Composable +private fun CallRingtoneDialog(state: NotificationSettingsState) { + val initialSelection = when (state.callRingtone.sound) { + NotificationSound.SystemDefault -> 0 + else -> null + } + val subtitle = if (initialSelection == null) { + stringResource( + id = R.string.screen_notification_settings_message_sound_dialog_current_subtitle, + state.callRingtone.displayName, + ) + } else { + null + } + SingleSelectionDialog( + title = stringResource(id = R.string.screen_notification_settings_call_ringtone_label), + subtitle = subtitle, + options = persistentListOf( + ListOption(title = stringResource(id = R.string.screen_notification_settings_sound_system_default)), + ListOption(title = stringResource(id = R.string.screen_notification_settings_message_sound_dialog_choose_other)), + ), + initialSelection = initialSelection, + onSelectOption = { index -> + when (index) { + 0 -> state.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + else -> state.eventSink(NotificationSettingsEvents.LaunchCallRingtonePicker) + } + }, + onDismissRequest = { state.eventSink(NotificationSettingsEvents.DismissCallRingtoneDialog) }, + ) +} + +@Composable +private fun SoundCopyErrorRow( + onDismissClick: () -> Unit, +) { + ListItem( + modifier = Modifier + .background(ElementTheme.colors.bgSubtleSecondary) + .semantics { liveRegion = LiveRegionMode.Polite }, + headlineContent = { + Text( + text = stringResource(R.string.screen_notification_settings_sound_set_sound_error_title), + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + ) + }, + trailingContent = ListItemContent.Custom { _ -> + IconButton(onClick = onDismissClick) { + Icon( + imageVector = CompoundIcons.Close(), + contentDescription = stringResource(R.string.screen_notification_settings_sound_set_sound_error_dismiss_a11y), + ) + } + }, + ) +} + +@Composable +private fun rememberSoundPickerOnClick( + type: Int, + current: NotificationSound, + defaultUri: Uri, + onSoundPicked: (NotificationSound) -> Unit, +): () -> Unit { + // Paparazzi previews don't provide a LocalActivityResultRegistryOwner, which + // rememberLauncherForActivityResult requires. Skip the launcher in inspection mode and + // return a no-op click handler — previews don't need to launch the picker. + if (LocalInspectionMode.current) return {} + val launcher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val sound = result.data?.toPickedNotificationSound(defaultUri) + if (sound != null) { + onSoundPicked(sound) + } + } + } + return { + launcher.launch(buildRingtonePickerIntent(type = type, current = current, defaultUri = defaultUri)) + } +} + @Composable private fun getTitleForRoomNotificationMode(mode: RoomNotificationMode?) = when (mode) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPicker.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPicker.kt new file mode 100644 index 0000000000..df9d8bb0c4 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPicker.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.preferences.impl.notifications + +import android.content.Intent +import android.media.RingtoneManager +import android.net.Uri +import androidx.core.content.IntentCompat +import androidx.core.net.toUri +import io.element.android.libraries.preferences.api.store.NotificationSound + +/** + * Builds an `ACTION_RINGTONE_PICKER` intent for [type] (notification or ringtone), pre-selecting + * the user's [current] choice and showing the system [defaultUri] as the "Default" option. + */ +internal fun buildRingtonePickerIntent( + type: Int, + current: NotificationSound, + defaultUri: Uri, +): Intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, type) + putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true) + putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true) + putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri) + val existingUri: Uri? = when (current) { + // ElementDefault and ElementFade refer to bundled raw resources, which the system picker + // can't enumerate. Fall back to the picker's "Default" row so the user has something selected. + NotificationSound.SystemDefault, + NotificationSound.ElementDefault, + NotificationSound.ElementFade -> defaultUri + NotificationSound.Silent -> null + is NotificationSound.Custom -> current.uri.toUri() + } + putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingUri) +} + +/** + * Maps the URI returned by the ringtone picker into a [NotificationSound]: + * - null URI → the user picked "Silent", + * - URI matching [defaultUri] → the user picked "Default", + * - any other URI → the user picked a specific ringtone. + */ +internal fun Intent.toPickedNotificationSound(defaultUri: Uri): NotificationSound { + val pickedUri: Uri? = IntentCompat.getParcelableExtra(this, RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java) + return when { + pickedUri == null -> NotificationSound.Silent + pickedUri == defaultUri -> NotificationSound.SystemDefault + else -> NotificationSound.Custom(pickedUri.toString()) + } +} diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 7389a0433c..f2a3d87770 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -58,6 +58,7 @@ "Feeling experimental?" "Labs" "Additional settings" + "Call ringtone" "Audio and video calls" "Configuration mismatch" "We’ve simplified Notifications Settings to make options easier to find. Some custom settings you’ve chosen in the past are not shown here, but they’re still active. @@ -76,10 +77,15 @@ If you proceed, some of your settings may change." "Invitations" "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms." "Mentions" + "Choose another sound…" + "Currently using %1$s" + "Message sound" + "Message sound" "All" "Mentions" "Notify me for" "Notify me on @room" + "Custom" "Custom sound…" "Error deleting file" "Element Default" @@ -87,7 +93,9 @@ If you proceed, some of your settings may change." "Error importing file" "Problem previewing alert sound" "Sound" + "Dismiss alert sound error" "Problem setting alert sound" + "Silent" "Alert" "Anticipate" "Bell" @@ -95,6 +103,7 @@ If you proceed, some of your settings may change." "Calypso" "Chime" "Choo Choo" + "System Default" "Descent" "Electronic" "Fanfare" diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt index 26bb0ba1ee..29e4b1c40a 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsPresenterTest.kt @@ -17,21 +17,35 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.push.api.PushService +import io.element.android.libraries.push.api.notifications.NotificationSoundUpdater +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier import io.element.android.libraries.push.test.FakePushService +import io.element.android.libraries.push.test.notifications.FakeNotificationSoundUpdater +import io.element.android.libraries.push.test.notifications.FakeSoundDisplayNameResolver +import io.element.android.libraries.push.test.notifications.sound.FakeNotificationSoundCopier import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider import io.element.android.libraries.pushproviders.test.FakePushProvider import io.element.android.libraries.pushstore.test.userpushstore.FakeUserPushStoreFactory +import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.consumeItemsUntilPredicate 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.flow.first import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Test import kotlin.time.Duration.Companion.milliseconds +@Suppress("LargeClass") class NotificationSettingsPresenterTest { @Test fun `present - ensures initial state is correct`() = runTest { @@ -353,10 +367,756 @@ class NotificationSettingsPresenterTest { ) } + @Test + fun `present - SetMessageSound atomically persists, increments version, and recreates channel`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore() + val customSound = NotificationSound.Custom("content://media/internal/audio/media/42") + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initialState.messageSound.sound).isEqualTo(NotificationSound.SystemDefault) + + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(customSound)) + + val updatedState = consumeItemsUntilPredicate { it.messageSound.sound == customSound }.last() + assertThat(updatedState.messageSound.sound).isEqualTo(customSound) + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSoundVersion).isEqualTo(1) + recreateRecorder.assertions().isCalledOnce().with(value(customSound), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SetCallRingtone with Silent atomically persists and recreates channel`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateRingingCallChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initialState.callRingtone.sound).isEqualTo(NotificationSound.SystemDefault) + + initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + + val updatedState = consumeItemsUntilPredicate { it.callRingtone.sound == NotificationSound.Silent }.last() + assertThat(updatedState.callRingtone.sound).isEqualTo(NotificationSound.Silent) + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtoneVersion).isEqualTo(1) + recreateRecorder.assertions().isCalledOnce().with(value(NotificationSound.Silent), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - successive message sound changes increment version each time`() = runTest { + val versions = mutableListOf() + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, version -> versions += version }, + ), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://a"))) + consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.Custom("content://a") } + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://b"))) + consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.Custom("content://b") } + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.SystemDefault } + + assertThat(versions).containsExactly(1, 2, 3).inOrder() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SetMessageSound with SystemDefault clears existing custom URI`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore(messageSound = NotificationSound.Custom("content://existing")) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + ) + presenter.test { + consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.sound == NotificationSound.Custom("content://existing") + }.last().eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + consumeItemsUntilPredicate { it.messageSound.sound == NotificationSound.SystemDefault } + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(NotificationSound.SystemDefault) + recreateRecorder.assertions().isCalledOnce().with(value(NotificationSound.SystemDefault), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SetMessageSound surfaces DataStore failure via changeNotificationSettingAction`() = runTest { + val backing = InMemoryAppPreferencesStore() + val store = object : AppPreferencesStore by backing { + override suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int { + error("Simulated DataStore failure") + } + } + val presenter = createNotificationSettingsPresenter(appPreferencesStore = store) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://x"))) + + val errorState = consumeItemsUntilPredicate { + it.changeNotificationSettingAction.isFailure() + }.last() + assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SystemDefault and Silent display names do not invoke the resolver`() = runTest { + val resolverCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.SystemDefault, + callRingtone = NotificationSound.Silent, + ), + soundDisplayNameResolver = FakeSoundDisplayNameResolver( + resolveLambda = { uri -> + resolverCalls += uri + "Should not appear" + }, + ), + ) + presenter.test { + // The synchronous branches (SystemDefault / Silent) feed the label directly without + // routing through produceState — so the resolver is never invoked, and the label + // never flashes from the default to itself. + consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(resolverCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - ElementDefault and ElementFade display names do not invoke the resolver`() = runTest { + val resolverCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.ElementFade, + callRingtone = NotificationSound.ElementDefault, + ), + soundDisplayNameResolver = FakeSoundDisplayNameResolver( + resolveLambda = { uri -> + resolverCalls += uri + "Should not appear" + }, + ), + ) + presenter.test { + consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(resolverCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - resolves Custom display name via SoundDisplayNameResolver`() = runTest { + val resolverCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore(messageSound = NotificationSound.Custom("content://media/42")), + soundDisplayNameResolver = FakeSoundDisplayNameResolver( + resolveLambda = { uri -> + resolverCalls += uri + "Pixel notification" + }, + ), + ) + presenter.test { + val state = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.displayName == "Pixel notification" + }.last() + assertThat(state.messageSound.displayName).isEqualTo("Pixel notification") + assertThat(resolverCalls).contains("content://media/42") + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - Custom display name falls back to localized Custom string when no persisted title and resolver returns null`() = runTest { + val resolverCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + // No persisted display name (legacy data path). Resolver returns null. Final fallback + // is the localised string resource (FakeStringProvider returns "A string" for any + // resource id) — never the default label, so the row is not mislabelled as "System default". + appPreferencesStore = InMemoryAppPreferencesStore(messageSound = NotificationSound.Custom("content://gone")), + soundDisplayNameResolver = FakeSoundDisplayNameResolver( + resolveLambda = { uri -> + resolverCalls += uri + null + }, + ), + ) + presenter.test { + val state = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.sound is NotificationSound.Custom && + it.messageSound.displayName == "A string" + }.last() + assertThat(state.messageSound.displayName).isEqualTo("A string") + assertThat(resolverCalls).contains("content://gone") + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - persisted in-app pick takes precedence over channel read`() = runTest { + // When DataStore holds a real in-app pick, the channel read must not run. Persisted + // value is what the user explicitly chose; channel state is only a fallback. + var channelReadCalls = 0 + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.Custom("content://persisted"), + messageSoundDisplayName = "Persisted Tone", + ), + notificationSoundUpdater = FakeNotificationSoundUpdater( + readNoisyChannelSoundLambda = { + channelReadCalls++ + NotificationSound.Custom("content://channel/should-not-appear") + }, + ), + ) + presenter.test { + val state = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.displayName == "Persisted Tone" + }.last() + assertThat(state.messageSound.sound).isEqualTo(NotificationSound.Custom("content://persisted")) + assertThat(channelReadCalls).isEqualTo(0) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - persisted display name takes precedence over live resolver probe`() = runTest { + val resolverCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.Custom("content://media/42"), + messageSoundDisplayName = "Captured at copy time", + ), + soundDisplayNameResolver = FakeSoundDisplayNameResolver( + resolveLambda = { uri -> + resolverCalls += uri + "Live probe (should not appear)" + }, + ), + ) + presenter.test { + val state = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.displayName == "Captured at copy time" + }.last() + assertThat(state.messageSound.displayName).isEqualTo("Captured at copy time") + // Live resolver must not be invoked when a persisted title is available. + assertThat(resolverCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - SetMessageSound with Custom copies via copier and persists the FileProvider URI`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + notificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { _, _ -> + NotificationSoundCopier.CopyResult.Success( + fileProviderUriString = "content://my.app.fileprovider/notification_sounds/message_sound.ogg", + displayName = "Cool Tone", + ) + }), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://media/source"))) + advanceUntilIdle() + + val expectedSound = NotificationSound.Custom("content://my.app.fileprovider/notification_sounds/message_sound.ogg") + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(expectedSound) + assertThat(appPreferencesStore.getMessageSoundDisplayNameFlow().first()).isEqualTo("Cool Tone") + recreateRecorder.assertions().isCalledOnce().with(value(expectedSound), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SetMessageSound copy failure surfaces inline error and leaves prior choice intact`() = runTest { + val priorChoice = NotificationSound.Custom("content://prior") + val appPreferencesStore = InMemoryAppPreferencesStore(messageSound = priorChoice) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { _, _ -> + NotificationSoundCopier.CopyResult.UnplayableSource + }), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://broken"))) + + val errorState = consumeItemsUntilPredicate { it.messageSound.copyError }.last() + assertThat(errorState.messageSound.copyError).isTrue() + // Persisted state is unchanged. + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(priorChoice) + + // Dismissing the inline error clears the flag without altering persistence. + errorState.eventSink(NotificationSettingsEvents.DismissMessageSoundCopyError) + val cleared = consumeItemsUntilPredicate { !it.messageSound.copyError }.last() + assertThat(cleared.messageSound.copyError).isFalse() + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound).isEqualTo(priorChoice) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - SetCallRingtone copy failure surfaces inline error and leaves prior choice intact`() = runTest { + val priorChoice = NotificationSound.Custom("content://prior") + val appPreferencesStore = InMemoryAppPreferencesStore(callRingtone = priorChoice) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { _, _ -> + NotificationSoundCopier.CopyResult.UnplayableSource + }), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Custom("content://broken"))) + + val errorState = consumeItemsUntilPredicate { it.callRingtone.copyError }.last() + assertThat(errorState.callRingtone.copyError).isTrue() + // Persisted state is unchanged. + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtone).isEqualTo(priorChoice) + + // Dismissing the inline error clears the flag without altering persistence. + errorState.eventSink(NotificationSettingsEvents.DismissCallRingtoneCopyError) + val cleared = consumeItemsUntilPredicate { !it.callRingtone.copyError }.last() + assertThat(cleared.callRingtone.copyError).isFalse() + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtone).isEqualTo(priorChoice) + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - SystemDefault and Silent picks bypass the copier`() = runTest { + val copierCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, _ -> }, + recreateRingingCallChannelLambda = { _, _ -> }, + ), + notificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { source, _ -> + copierCalls += source + NotificationSoundCopier.CopyResult.Success(source, "x") + }), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + advanceUntilIdle() + assertThat(copierCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - non-Custom picks delete the stored slot file`() = runTest { + val deleteCalls = mutableListOf() + // Start from persisted Custom for both slots so the non-Custom picks below are real + // transitions (no-op short-circuit only fires when the new sound equals the persisted one). + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.Custom("content://prior-message"), + callRingtone = NotificationSound.Custom("content://prior-call"), + ), + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, _ -> }, + recreateRingingCallChannelLambda = { _, _ -> }, + ), + notificationSoundCopier = FakeNotificationSoundCopier( + copyLambda = { _, _ -> NotificationSoundCopier.CopyResult.Failure(IllegalStateException("not expected")) }, + deleteStoredSoundForLambda = { slot -> deleteCalls += slot }, + ), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.SystemDefault)) + initialState.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + advanceUntilIdle() + assertThat(deleteCalls).containsExactly( + NotificationSoundCopier.SoundSlot.Message, + NotificationSoundCopier.SoundSlot.Call, + ) + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - re-selecting the persisted non-Custom sound is a no-op`() = runTest { + val recreateCalls = mutableListOf() + val deleteCalls = mutableListOf() + val copierCalls = mutableListOf() + val appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.ElementDefault, + messageSoundChannelVersion = 5, + callRingtone = NotificationSound.Silent, + callRingtoneChannelVersion = 3, + ) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundCopier = FakeNotificationSoundCopier( + copyLambda = { source, _ -> + copierCalls += source + NotificationSoundCopier.CopyResult.Success(source, "title") + }, + deleteStoredSoundForLambda = { slot -> deleteCalls += slot }, + ), + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, version -> recreateCalls += version }, + recreateRingingCallChannelLambda = { _, version -> recreateCalls += version }, + ), + ) + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + + // Re-pick the persisted sound for both slots — neither should churn the channel, + // bump the version, or hit the copier / delete sweep. + initial.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + initial.eventSink(NotificationSettingsEvents.SetCallRingtone(NotificationSound.Silent)) + advanceUntilIdle() + + val config = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(config.messageSoundVersion).isEqualTo(5) + assertThat(config.callRingtoneVersion).isEqualTo(3) + assertThat(recreateCalls).isEmpty() + assertThat(copierCalls).isEmpty() + assertThat(deleteCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - rapid back-to-back picks settle in deterministic order`() = runTest { + // Defends the persist+recreate window against rapid event firing (e.g. an accessibility + // service driving the dialog). The per-slot mutex in the presenter forces copy → persist + // → recreate to complete for the first pick before the second begins, so the recreated + // version sequence matches the persisted version sequence. + val recreatedVersions = mutableListOf() + val copyOrder = mutableListOf() + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { source, _ -> + copyOrder += source + NotificationSoundCopier.CopyResult.Success(source, "title") + }), + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, version -> recreatedVersions += version }, + ), + ) + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initial.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://A"))) + initial.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://B"))) + advanceUntilIdle() + + assertThat(copyOrder).containsExactly("content://A", "content://B").inOrder() + assertThat(recreatedVersions).containsExactly(1, 2).inOrder() + val config = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(config.messageSoundVersion).isEqualTo(2) + assertThat(config.messageSound).isEqualTo(NotificationSound.Custom("content://B")) + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - Custom pick does not invoke deleteStoredSoundFor (copier sweeps inline)`() = runTest { + val deleteCalls = mutableListOf() + val presenter = createNotificationSettingsPresenter( + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { _, _ -> }, + ), + notificationSoundCopier = FakeNotificationSoundCopier( + copyLambda = { _, _ -> + NotificationSoundCopier.CopyResult.Success( + fileProviderUriString = "content://my.app.fileprovider/notification_sounds/message_sound.mp3", + displayName = "New Tone", + ) + }, + deleteStoredSoundForLambda = { slot -> deleteCalls += slot }, + ), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initialState.eventSink(NotificationSettingsEvents.SetMessageSound(NotificationSound.Custom("content://media/source"))) + advanceUntilIdle() + assertThat(deleteCalls).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - ShowMessageSoundDialog and DismissMessageSoundDialog toggle dialog visibility`() = runTest { + val presenter = createNotificationSettingsPresenter() + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initial.showMessageSoundDialog).isFalse() + + initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() + assertThat(shown.showMessageSoundDialog).isTrue() + + shown.eventSink(NotificationSettingsEvents.DismissMessageSoundDialog) + val hidden = consumeItemsUntilPredicate { !it.showMessageSoundDialog }.last() + assertThat(hidden.showMessageSoundDialog).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - SelectMessageSoundPreset persists ElementDefault, recreates the channel and closes the dialog`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore() + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateNoisyChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + ) + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() + + shown.eventSink(NotificationSettingsEvents.SelectMessageSoundPreset(NotificationSound.ElementDefault)) + advanceUntilIdle() + consumeItemsUntilPredicate { + !it.showMessageSoundDialog && it.messageSound.sound == NotificationSound.ElementDefault + } + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().messageSound) + .isEqualTo(NotificationSound.ElementDefault) + recreateRecorder.assertions().isCalledOnce().with(value(NotificationSound.ElementDefault), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - LaunchMessageSoundPicker increments the picker token and closes the dialog`() = runTest { + val presenter = createNotificationSettingsPresenter() + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initial.pendingMessageSoundPickerLaunch).isEqualTo(0) + initial.eventSink(NotificationSettingsEvents.ShowMessageSoundDialog) + val shown = consumeItemsUntilPredicate { it.showMessageSoundDialog }.last() + + shown.eventSink(NotificationSettingsEvents.LaunchMessageSoundPicker) + val launched = consumeItemsUntilPredicate { it.pendingMessageSoundPickerLaunch == 1 }.last() + assertThat(launched.showMessageSoundDialog).isFalse() + assertThat(launched.pendingMessageSoundPickerLaunch).isEqualTo(1) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - ShowCallRingtoneDialog and DismissCallRingtoneDialog toggle dialog visibility`() = runTest { + val presenter = createNotificationSettingsPresenter() + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initial.showCallRingtoneDialog).isFalse() + + initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() + assertThat(shown.showCallRingtoneDialog).isTrue() + + shown.eventSink(NotificationSettingsEvents.DismissCallRingtoneDialog) + val hidden = consumeItemsUntilPredicate { !it.showCallRingtoneDialog }.last() + assertThat(hidden.showCallRingtoneDialog).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - SelectCallRingtonePreset persists SystemDefault, recreates the channel and closes the dialog`() = runTest { + val recreateRecorder = lambdaRecorder { _, _ -> } + val appPreferencesStore = InMemoryAppPreferencesStore(callRingtone = NotificationSound.Silent) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateRingingCallChannelLambda = { sound, version -> recreateRecorder(sound, version) }, + ), + ) + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.callRingtone.sound == NotificationSound.Silent + }.last() + initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() + + shown.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + advanceUntilIdle() + consumeItemsUntilPredicate { + !it.showCallRingtoneDialog && it.callRingtone.sound == NotificationSound.SystemDefault + } + assertThat(appPreferencesStore.getNotificationSoundChannelConfig().callRingtone) + .isEqualTo(NotificationSound.SystemDefault) + recreateRecorder.assertions().isCalledOnce().with(value(NotificationSound.SystemDefault), value(1)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - LaunchCallRingtonePicker increments the picker token and closes the dialog`() = runTest { + val presenter = createNotificationSettingsPresenter() + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid + }.last() + assertThat(initial.pendingCallRingtonePickerLaunch).isEqualTo(0) + initial.eventSink(NotificationSettingsEvents.ShowCallRingtoneDialog) + val shown = consumeItemsUntilPredicate { it.showCallRingtoneDialog }.last() + + shown.eventSink(NotificationSettingsEvents.LaunchCallRingtonePicker) + val launched = consumeItemsUntilPredicate { it.pendingCallRingtonePickerLaunch == 1 }.last() + assertThat(launched.showCallRingtoneDialog).isFalse() + assertThat(launched.pendingCallRingtonePickerLaunch).isEqualTo(1) + cancelAndIgnoreRemainingEvents() + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - SelectCallRingtonePreset clears legacy ringtone classification`() = runTest { + // Legacy state: persisted SystemDefault + channel version 0 means the presenter probes the + // channel and surfaces whatever Android system has. We seed that probe with Silent so the + // initial row reads Silent even though DataStore holds SystemDefault. A preset pick must + // null the legacy classifier; otherwise re-reading state would still resolve to Silent + // after the SystemDefault selection persists. + val appPreferencesStore = InMemoryAppPreferencesStore( + callRingtone = NotificationSound.SystemDefault, + callRingtoneChannelVersion = 0, + ) + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = FakeNotificationSoundUpdater( + recreateRingingCallChannelLambda = { _, _ -> }, + readRingingCallChannelSoundLambda = { NotificationSound.Silent }, + ), + ) + presenter.test { + val initial = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.callRingtone.sound == NotificationSound.Silent + }.last() + initial.eventSink(NotificationSettingsEvents.SelectCallRingtonePreset(NotificationSound.SystemDefault)) + advanceUntilIdle() + val after = consumeItemsUntilPredicate { + it.callRingtone.sound == NotificationSound.SystemDefault + }.last() + assertThat(after.callRingtone.sound).isEqualTo(NotificationSound.SystemDefault) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - persisted ElementDefault renders Element default label`() = runTest { + val presenter = createNotificationSettingsPresenter( + appPreferencesStore = InMemoryAppPreferencesStore(messageSound = NotificationSound.ElementDefault), + ) + presenter.test { + val state = consumeItemsUntilPredicate { + it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid && + it.messageSound.sound == NotificationSound.ElementDefault + }.last() + // FakeStringProvider returns "A string" for any resource, so this just verifies the + // ElementDefault branch is taken (vs falling through to the Custom resolver path). + assertThat(state.messageSound.displayName).isEqualTo("A string") + cancelAndIgnoreRemainingEvents() + } + } + private fun TestScope.createNotificationSettingsPresenter( notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), pushService: PushService = FakePushService(), fullScreenIntentPermissionsStateLambda: () -> FullScreenIntentPermissionsState = { aFullScreenIntentPermissionsState() }, + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), + notificationSoundUpdater: NotificationSoundUpdater = FakeNotificationSoundUpdater(), + // Default copier passes the picked URI string straight through as the FileProvider URI + // and uses the URI string as the display name. Tests that exercise copy failures override. + notificationSoundCopier: NotificationSoundCopier = FakeNotificationSoundCopier(copyLambda = { source, _ -> + NotificationSoundCopier.CopyResult.Success(source, source) + }), + soundDisplayNameResolver: FakeSoundDisplayNameResolver = FakeSoundDisplayNameResolver(), ): NotificationSettingsPresenter { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) return NotificationSettingsPresenter( @@ -366,6 +1126,11 @@ class NotificationSettingsPresenterTest { pushService = pushService, systemNotificationsEnabledProvider = FakeSystemNotificationsEnabledProvider(), fullScreenIntentPermissionsPresenter = { fullScreenIntentPermissionsStateLambda() }, + appPreferencesStore = appPreferencesStore, + notificationSoundUpdater = notificationSoundUpdater, + notificationSoundCopier = notificationSoundCopier, + soundDisplayNameResolver = soundDisplayNameResolver, + stringProvider = FakeStringProvider(), sessionCoroutineScope = backgroundScope, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt index 66ed0339a3..dee0df511c 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsViewTest.kt @@ -13,6 +13,7 @@ package io.element.android.features.preferences.impl.notifications import androidx.activity.ComponentActivity import androidx.compose.ui.test.AndroidComposeUiTest import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.v2.runAndroidComposeUiTest @@ -49,7 +50,7 @@ class NotificationSettingsViewTest { eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled) } - @Config(qualifiers = "h1024dp") + @Config(qualifiers = "h1280dp") @Test fun `clicking on troubleshoot notification invokes the expected callback`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() @@ -241,7 +242,7 @@ class NotificationSettingsViewTest { ) } - @Config(qualifiers = "h1024dp") + @Config(qualifiers = "h1280dp") @Test fun `clicking on Push notification provider emits the expected event`() = runAndroidComposeUiTest { val eventsRecorder = EventsRecorder() @@ -277,6 +278,43 @@ class NotificationSettingsViewTest { ) ) } + + @Config(qualifiers = "h1280dp") + @Test + fun `sounds preference category renders rows with current display names`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setNotificationSettingsView( + state = aValidNotificationSettingsState( + eventSink = eventsRecorder, + messageSoundDisplayName = "Pixel notification", + callRingtoneDisplayName = "Pixel ringtone", + ), + ) + onNodeWithText("Sound").assertIsDisplayed() + onNodeWithText("Message sound").assertIsDisplayed() + onNodeWithText("Call ringtone").assertIsDisplayed() + onNodeWithText("Pixel notification").assertIsDisplayed() + onNodeWithText("Pixel ringtone").assertIsDisplayed() + } + + @Config(qualifiers = "h1280dp") + @Test + fun `clicking the message sound row opens the preset dialog`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + setNotificationSettingsView( + state = aValidNotificationSettingsState(eventSink = eventsRecorder), + ) + // The click now opens the in-app preset dialog instead of launching the system picker + // directly; the picker only fires from the dialog's "Choose another sound..." option + // (covered by Presenter tests + NotificationSoundPickerTest). + onNodeWithText("Message sound").performClick() + eventsRecorder.assertList( + listOf( + NotificationSettingsEvents.RefreshSystemNotificationsEnabled, + NotificationSettingsEvents.ShowMessageSoundDialog, + ) + ) + } } private fun AndroidComposeUiTest.setNotificationSettingsView( diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPickerTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPickerTest.kt new file mode 100644 index 0000000000..3031187f25 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundPickerTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.preferences.impl.notifications + +import android.content.Intent +import android.media.RingtoneManager +import android.net.Uri +import android.provider.Settings +import androidx.core.net.toUri +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.preferences.api.store.NotificationSound +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class NotificationSoundPickerTest { + @Test + fun `buildRingtonePickerIntent encodes SystemDefault as the system default URI`() { + val intent = buildRingtonePickerIntent( + type = RingtoneManager.TYPE_NOTIFICATION, + current = NotificationSound.SystemDefault, + defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, + ) + assertThat(intent.action).isEqualTo(RingtoneManager.ACTION_RINGTONE_PICKER) + assertThat(intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1)).isEqualTo(RingtoneManager.TYPE_NOTIFICATION) + assertThat(intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false)).isTrue() + assertThat(intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)).isTrue() + @Suppress("DEPRECATION") + val existing: Uri? = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI) + assertThat(existing).isEqualTo(Settings.System.DEFAULT_NOTIFICATION_URI) + } + + @Test + fun `buildRingtonePickerIntent encodes Silent as a null existing URI`() { + val intent = buildRingtonePickerIntent( + type = RingtoneManager.TYPE_RINGTONE, + current = NotificationSound.Silent, + defaultUri = Settings.System.DEFAULT_RINGTONE_URI, + ) + @Suppress("DEPRECATION") + val existing: Uri? = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI) + assertThat(existing).isNull() + } + + @Test + fun `buildRingtonePickerIntent encodes Custom as the parsed URI`() { + val intent = buildRingtonePickerIntent( + type = RingtoneManager.TYPE_NOTIFICATION, + current = NotificationSound.Custom("content://media/internal/audio/media/42"), + defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, + ) + @Suppress("DEPRECATION") + val existing: Uri? = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI) + assertThat(existing).isEqualTo("content://media/internal/audio/media/42".toUri()) + } + + @Test + fun `toPickedNotificationSound maps null URI to Silent`() { + val result = Intent().apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, null as Uri?) + } + assertThat(result.toPickedNotificationSound(Settings.System.DEFAULT_NOTIFICATION_URI)) + .isEqualTo(NotificationSound.Silent) + } + + @Test + fun `toPickedNotificationSound maps default URI to SystemDefault`() { + val result = Intent().apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Settings.System.DEFAULT_NOTIFICATION_URI) + } + assertThat(result.toPickedNotificationSound(Settings.System.DEFAULT_NOTIFICATION_URI)) + .isEqualTo(NotificationSound.SystemDefault) + } + + @Test + fun `toPickedNotificationSound maps any other URI to Custom`() { + val customUri = "content://media/internal/audio/media/42".toUri() + val result = Intent().apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, customUri) + } + assertThat(result.toPickedNotificationSound(Settings.System.DEFAULT_NOTIFICATION_URI)) + .isEqualTo(NotificationSound.Custom(customUri.toString())) + } +} diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundStorageTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundStorageTest.kt new file mode 100644 index 0000000000..7f8dba8226 --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSoundStorageTest.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.preferences.impl.notifications + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.preferences.api.store.NotificationSound.Companion.toStored +import org.junit.Test + +class NotificationSoundStorageTest { + @Test + fun `fromStored - null maps to SystemDefault`() { + assertThat(NotificationSound.fromStored(null)).isEqualTo(NotificationSound.SystemDefault) + } + + @Test + fun `fromStored - silent sentinel maps to Silent`() { + assertThat(NotificationSound.fromStored("silent")).isEqualTo(NotificationSound.Silent) + } + + @Test + fun `fromStored - element_default sentinel maps to ElementDefault`() { + assertThat(NotificationSound.fromStored("element_default")).isEqualTo(NotificationSound.ElementDefault) + } + + @Test + fun `fromStored - element_fade sentinel maps to ElementFade`() { + assertThat(NotificationSound.fromStored("element_fade")).isEqualTo(NotificationSound.ElementFade) + } + + @Test + fun `fromStored - any other string is treated as a Custom URI`() { + assertThat(NotificationSound.fromStored("content://media/42")) + .isEqualTo(NotificationSound.Custom("content://media/42")) + } + + @Test + fun `toStored - SystemDefault encodes as null`() { + assertThat(NotificationSound.SystemDefault.toStored()).isNull() + } + + @Test + fun `toStored - ElementDefault encodes as element_default`() { + assertThat(NotificationSound.ElementDefault.toStored()).isEqualTo("element_default") + } + + @Test + fun `toStored - ElementFade encodes as element_fade`() { + assertThat(NotificationSound.ElementFade.toStored()).isEqualTo("element_fade") + } + + @Test + fun `toStored - Silent encodes as silent`() { + assertThat(NotificationSound.Silent.toStored()).isEqualTo("silent") + } + + @Test + fun `toStored - Custom encodes as the URI string`() { + assertThat(NotificationSound.Custom("content://x").toStored()).isEqualTo("content://x") + } + + @Test + fun `round-trip preserves every variant`() { + val variants = listOf( + NotificationSound.SystemDefault, + NotificationSound.ElementDefault, + NotificationSound.ElementFade, + NotificationSound.Silent, + NotificationSound.Custom("content://media/42"), + ) + for (sound in variants) { + assertThat(NotificationSound.fromStored(sound.toStored())).isEqualTo(sound) + } + } +} diff --git a/libraries/preferences/api/build.gradle.kts b/libraries/preferences/api/build.gradle.kts index a441616657..7084c960c6 100644 --- a/libraries/preferences/api/build.gradle.kts +++ b/libraries/preferences/api/build.gradle.kts @@ -9,7 +9,7 @@ import extension.testCommonDependencies */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index df8dc8acc9..b1877b3ae5 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -41,5 +41,27 @@ interface AppPreferencesStore { suspend fun setTracingLogPacks(targets: Set) fun getTracingLogPacksFlow(): Flow> + fun getMessageSoundFlow(): Flow + + /** + * Atomically persists [sound] (with copy-time [title] for Custom; cleared otherwise) and + * bumps the channel version. Single transaction so process death can't desync URI and version. + */ + suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int + + /** Title captured at copy time. Null for SystemDefault / Silent or pre-title persisted data. */ + fun getMessageSoundDisplayNameFlow(): Flow + + fun getCallRingtoneFlow(): Flow + + /** See [setMessageSoundAndIncrementVersion]. */ + suspend fun setCallRingtoneAndIncrementVersion(sound: NotificationSound, title: String?): Int + + /** See [getMessageSoundDisplayNameFlow]. */ + fun getCallRingtoneDisplayNameFlow(): Flow + + /** Single-snapshot read of all sound prefs; used at boot to seed channels without N reads. */ + suspend fun getNotificationSoundChannelConfig(): NotificationSoundChannelConfig + suspend fun reset() } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/NotificationSound.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/NotificationSound.kt new file mode 100644 index 0000000000..f138dd592b --- /dev/null +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/NotificationSound.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.preferences.api.store + +import androidx.compose.runtime.Immutable + +/** + * The user's choice of notification sound for a notification channel. + */ +@Immutable +sealed interface NotificationSound { + /** Use the system's default tone for this channel (e.g. notification or ringtone). */ + data object SystemDefault : NotificationSound + + /** + * Use the bundled in-app sound. Semantically meaningful only for the noisy message channel, + * where it resolves to `R.raw.message`; the ringing-call channel has no bundled tone and + * treats this the same as [SystemDefault]. + */ + data object ElementDefault : NotificationSound + + /** + * Use the bundled in-app fade sound. Like [ElementDefault] this is meaningful only for the + * noisy message channel (`R.raw.element_fade`); the ringing-call channel has no bundled tone + * and treats this the same as [SystemDefault]. + */ + data object ElementFade : NotificationSound + + /** Produce no sound. */ + data object Silent : NotificationSound + + /** Use the ringtone at [uri]. */ + data class Custom(val uri: String) : NotificationSound + + companion object { + // String? round-trip used by [AppPreferencesStore]: + // null -> SystemDefault, "silent" -> Silent, "element_default" -> ElementDefault, + // "element_fade" -> ElementFade, else -> Custom(uri). + private const val STORED_SILENT = "silent" + private const val STORED_ELEMENT_DEFAULT = "element_default" + private const val STORED_ELEMENT_FADE = "element_fade" + + fun fromStored(value: String?): NotificationSound = when (value) { + null -> SystemDefault + STORED_SILENT -> Silent + STORED_ELEMENT_DEFAULT -> ElementDefault + STORED_ELEMENT_FADE -> ElementFade + else -> Custom(value) + } + + fun NotificationSound.toStored(): String? = when (this) { + SystemDefault -> null + Silent -> STORED_SILENT + ElementDefault -> STORED_ELEMENT_DEFAULT + ElementFade -> STORED_ELEMENT_FADE + is Custom -> uri + } + } +} + +/** + * Snapshot of the persisted notification-sound state, returned by + * [AppPreferencesStore.getNotificationSoundChannelConfig] in a single read. + */ +data class NotificationSoundChannelConfig( + val messageSound: NotificationSound, + val messageSoundVersion: Int, + val messageSoundDisplayName: String?, + val callRingtone: NotificationSound, + val callRingtoneVersion: Int, + val callRingtoneDisplayName: String?, +) diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index 44260461da..af35ac3c44 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -20,8 +20,12 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewValue import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.preferences.api.store.NotificationSound.Companion.toStored +import io.element.android.libraries.preferences.api.store.NotificationSoundChannelConfig import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map private val developerModeKey = booleanPreferencesKey("developerMode") @@ -32,6 +36,12 @@ private val timelineMediaPreviewValueKey = stringPreferencesKey("timelineMediaPr private val liveLocationMinimumDistanceUpdateKey = intPreferencesKey("liveLocationMinimumDistanceUpdate") private val logLevelKey = stringPreferencesKey("logLevel") private val traceLogPacksKey = stringPreferencesKey("traceLogPacks") +private val messageSoundUriKey = stringPreferencesKey("notificationMessageSoundUri") +private val messageSoundChannelVersionKey = intPreferencesKey("notificationMessageSoundChannelVersion") +private val messageSoundDisplayNameKey = stringPreferencesKey("notificationMessageSoundDisplayName") +private val callRingtoneUriKey = stringPreferencesKey("notificationCallRingtoneUri") +private val callRingtoneChannelVersionKey = intPreferencesKey("notificationCallRingtoneChannelVersion") +private val callRingtoneDisplayNameKey = stringPreferencesKey("notificationCallRingtoneDisplayName") @ContributesBinding(AppScope::class) class DefaultAppPreferencesStore( @@ -158,6 +168,75 @@ class DefaultAppPreferencesStore( } } + override fun getMessageSoundFlow(): Flow { + return store.data.map { prefs -> NotificationSound.fromStored(prefs[messageSoundUriKey]) } + } + + override suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int { + var newVersion = 0 + store.edit { prefs -> + val stored = sound.toStored() + if (stored != null) { + prefs[messageSoundUriKey] = stored + } else { + prefs.remove(messageSoundUriKey) + } + // Clear title on non-Custom so the picker doesn't show a stale label after a revert. + if (sound is NotificationSound.Custom && !title.isNullOrBlank()) { + prefs[messageSoundDisplayNameKey] = title + } else { + prefs.remove(messageSoundDisplayNameKey) + } + newVersion = (prefs[messageSoundChannelVersionKey] ?: 0) + 1 + prefs[messageSoundChannelVersionKey] = newVersion + } + return newVersion + } + + override fun getMessageSoundDisplayNameFlow(): Flow { + return store.data.map { prefs -> prefs[messageSoundDisplayNameKey] } + } + + override fun getCallRingtoneFlow(): Flow { + return store.data.map { prefs -> NotificationSound.fromStored(prefs[callRingtoneUriKey]) } + } + + override suspend fun setCallRingtoneAndIncrementVersion(sound: NotificationSound, title: String?): Int { + var newVersion = 0 + store.edit { prefs -> + val stored = sound.toStored() + if (stored != null) { + prefs[callRingtoneUriKey] = stored + } else { + prefs.remove(callRingtoneUriKey) + } + if (sound is NotificationSound.Custom && !title.isNullOrBlank()) { + prefs[callRingtoneDisplayNameKey] = title + } else { + prefs.remove(callRingtoneDisplayNameKey) + } + newVersion = (prefs[callRingtoneChannelVersionKey] ?: 0) + 1 + prefs[callRingtoneChannelVersionKey] = newVersion + } + return newVersion + } + + override fun getCallRingtoneDisplayNameFlow(): Flow { + return store.data.map { prefs -> prefs[callRingtoneDisplayNameKey] } + } + + override suspend fun getNotificationSoundChannelConfig(): NotificationSoundChannelConfig { + val prefs = store.data.first() + return NotificationSoundChannelConfig( + messageSound = NotificationSound.fromStored(prefs[messageSoundUriKey]), + messageSoundVersion = prefs[messageSoundChannelVersionKey] ?: 0, + messageSoundDisplayName = prefs[messageSoundDisplayNameKey], + callRingtone = NotificationSound.fromStored(prefs[callRingtoneUriKey]), + callRingtoneVersion = prefs[callRingtoneChannelVersionKey] ?: 0, + callRingtoneDisplayName = prefs[callRingtoneDisplayNameKey], + ) + } + override suspend fun reset() { store.edit { it.clear() } } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt index 152de12e99..6499e70223 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt @@ -12,8 +12,11 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewValue import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.preferences.api.store.NotificationSoundChannelConfig import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.updateAndGet class InMemoryAppPreferencesStore( isDeveloperModeEnabled: Boolean = false, @@ -23,16 +26,28 @@ class InMemoryAppPreferencesStore( theme: String? = null, liveLocationMinimumDistanceUpdate: Int = 10, logLevel: LogLevel = LogLevel.INFO, - traceLockPacks: Set = emptySet(), + traceLogPacks: Set = emptySet(), + messageSound: NotificationSound = NotificationSound.SystemDefault, + messageSoundChannelVersion: Int = 0, + messageSoundDisplayName: String? = null, + callRingtone: NotificationSound = NotificationSound.SystemDefault, + callRingtoneChannelVersion: Int = 0, + callRingtoneDisplayName: String? = null, ) : AppPreferencesStore { private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled) private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl) private val theme = MutableStateFlow(theme) private val liveLocationMinimumDistanceUpdate = MutableStateFlow(liveLocationMinimumDistanceUpdate) private val logLevel = MutableStateFlow(logLevel) - private val tracingLogPacks = MutableStateFlow(traceLockPacks) + private val tracingLogPacks = MutableStateFlow(traceLogPacks) private val hideInviteAvatars = MutableStateFlow(hideInviteAvatars) private val timelineMediaPreviewValue = MutableStateFlow(timelineMediaPreviewValue) + private val messageSound = MutableStateFlow(messageSound) + private val messageSoundChannelVersion = MutableStateFlow(messageSoundChannelVersion) + private val messageSoundDisplayName = MutableStateFlow(messageSoundDisplayName) + private val callRingtone = MutableStateFlow(callRingtone) + private val callRingtoneChannelVersion = MutableStateFlow(callRingtoneChannelVersion) + private val callRingtoneDisplayName = MutableStateFlow(callRingtoneDisplayName) override suspend fun setDeveloperModeEnabled(enabled: Boolean) { isDeveloperModeEnabled.value = enabled @@ -102,6 +117,45 @@ class InMemoryAppPreferencesStore( return tracingLogPacks } + override fun getMessageSoundFlow(): Flow { + return messageSound + } + + override suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int { + messageSound.value = sound + messageSoundDisplayName.value = if (sound is NotificationSound.Custom && !title.isNullOrBlank()) title else null + return messageSoundChannelVersion.updateAndGet { it + 1 } + } + + override fun getMessageSoundDisplayNameFlow(): Flow { + return messageSoundDisplayName + } + + override fun getCallRingtoneFlow(): Flow { + return callRingtone + } + + override suspend fun setCallRingtoneAndIncrementVersion(sound: NotificationSound, title: String?): Int { + callRingtone.value = sound + callRingtoneDisplayName.value = if (sound is NotificationSound.Custom && !title.isNullOrBlank()) title else null + return callRingtoneChannelVersion.updateAndGet { it + 1 } + } + + override fun getCallRingtoneDisplayNameFlow(): Flow { + return callRingtoneDisplayName + } + + override suspend fun getNotificationSoundChannelConfig(): NotificationSoundChannelConfig { + return NotificationSoundChannelConfig( + messageSound = messageSound.value, + messageSoundVersion = messageSoundChannelVersion.value, + messageSoundDisplayName = messageSoundDisplayName.value, + callRingtone = callRingtone.value, + callRingtoneVersion = callRingtoneChannelVersion.value, + callRingtoneDisplayName = callRingtoneDisplayName.value, + ) + } + override suspend fun reset() { // No op } diff --git a/libraries/push/api/build.gradle.kts b/libraries/push/api/build.gradle.kts index df6ac6120a..91e545daed 100644 --- a/libraries/push/api/build.gradle.kts +++ b/libraries/push/api/build.gradle.kts @@ -20,5 +20,6 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixmedia.api) + implementation(projects.libraries.preferences.api) implementation(projects.libraries.pushproviders.api) } diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt new file mode 100644 index 0000000000..b23c4122ad --- /dev/null +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationSoundUpdater.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.api.notifications + +import io.element.android.libraries.preferences.api.store.NotificationSound + +/** + * Reads and writes the sound on the message / ringing-call channels. Writes create a new + * versioned channel because Android forbids mutating sound after creation. + */ +interface NotificationSoundUpdater { + fun recreateNoisyChannel(sound: NotificationSound, version: Int) + + fun recreateRingingCallChannel(sound: NotificationSound, version: Int) + + /** Current channel sound classified into [NotificationSound]. Null when the channel doesn't exist. */ + suspend fun readNoisyChannelSound(): NotificationSound? + + /** See [readNoisyChannelSound]. */ + suspend fun readRingingCallChannelSound(): NotificationSound? +} diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/SoundDisplayNameResolver.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/SoundDisplayNameResolver.kt new file mode 100644 index 0000000000..a850b3e435 --- /dev/null +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/SoundDisplayNameResolver.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.api.notifications + +/** + * Resolves the user-facing title of a custom notification sound URI (the title shown by the + * system ringtone picker). Lifts the [android.media.RingtoneManager] side effect out of UI code + * so views and presenters stay testable. + */ +interface SoundDisplayNameResolver { + /** Returns the title of the ringtone at [uri], or null if it cannot be resolved. */ + suspend fun resolveCustomSoundTitle(uri: String): String? +} diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt new file mode 100644 index 0000000000..2268198353 --- /dev/null +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/sound/NotificationSoundCopier.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.api.notifications.sound + +/** + * Copies a picked notification sound into app-private storage and re-exposes it via the app's + * FileProvider, so the persisted reference outlives the original source. + */ +interface NotificationSoundCopier { + suspend fun copyToAppFiles(sourceUriString: String, slot: SoundSlot): CopyResult + + /** + * Removes any previously copied file for [slot] from app-private storage. No-op when no file + * exists. Call this when persisting a non-[NotificationSound.Custom] pick (SystemDefault / + * Silent) so the previous Custom copy doesn't linger as orphaned bytes — [copyToAppFiles] + * already sweeps stale files inline for Custom-to-Custom transitions. + */ + suspend fun deleteStoredSoundFor(slot: SoundSlot) + + enum class SoundSlot { Message, Call } + + sealed interface CopyResult { + /** + * @property fileProviderUriString FileProvider URI to persist as the channel sound. + * @property displayName [android.media.Ringtone.getTitle] for the source, or null. The + * picker substitutes a localised fallback label, so localisation stays in the UI layer. + */ + data class Success(val fileProviderUriString: String, val displayName: String?) : CopyResult + + /** [android.media.RingtoneManager] could not open the source URI as a Ringtone. */ + data object UnplayableSource : CopyResult + + /** The bytes copied successfully, but the resulting file is not a playable Ringtone. */ + data object UnplayableCopy : CopyResult + + /** Source exceeded the 5 MB cap. */ + data object FileTooLarge : CopyResult + + /** Any other I/O or unexpected failure. */ + data class Failure(val cause: Throwable) : CopyResult + } +} diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index e8acda59da..b9082c50f6 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.troubleshoot.api) implementation(projects.libraries.workmanager.api) + implementation(projects.features.announcement.api) implementation(projects.features.call.api) implementation(projects.features.enterprise.api) implementation(projects.features.lockscreen.api) @@ -80,6 +81,7 @@ dependencies { testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.troubleshoot.test) testImplementation(projects.libraries.workmanager.test) + testImplementation(projects.features.announcement.test) testImplementation(projects.features.call.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.lockscreen.test) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSoundDisplayNameResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSoundDisplayNameResolver.kt new file mode 100644 index 0000000000..b6237aba03 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultSoundDisplayNameResolver.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications + +import android.content.Context +import android.media.RingtoneManager +import androidx.core.net.toUri +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.push.api.notifications.SoundDisplayNameResolver +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +@ContributesBinding(AppScope::class) +class DefaultSoundDisplayNameResolver( + @ApplicationContext private val context: Context, +) : SoundDisplayNameResolver { + override suspend fun resolveCustomSoundTitle(uri: String): String? = withContext(Dispatchers.IO) { + val parsed = runCatchingExceptions { uri.toUri() }.getOrNull() ?: return@withContext null + // Probing our own FileProvider URI yields the internal filename ("call_sound.ogg"), + // not a user-meaningful tone name — better to fall through to the localised "Custom" + // label in the UI. + if (parsed.authority == context.notificationSoundFileProviderAuthority()) return@withContext null + runCatchingExceptions { + RingtoneManager.getRingtone(context, parsed)?.getTitle(context) + }.getOrNull() + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationSoundFileProviderAuthority.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationSoundFileProviderAuthority.kt new file mode 100644 index 0000000000..db9fa47c02 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationSoundFileProviderAuthority.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications + +import android.content.Context + +// Mirrors the FileProvider authority declared in AndroidManifest.xml as +// `${applicationId}.fileprovider`. Kept here so the resolver's skip-rule and the copier's URI +// construction never drift apart. +internal const val NOTIFICATION_SOUND_FILE_PROVIDER_AUTHORITY_SUFFIX = ".fileprovider" + +internal fun Context.notificationSoundFileProviderAuthority(): String = + packageName + NOTIFICATION_SOUND_FILE_PROVIDER_AUTHORITY_SUFFIX diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt index dc009c0b7d..8d9b776694 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.push.impl.notifications.channels import android.content.ContentResolver import android.content.Context +import android.content.Intent import android.media.AudioAttributes import android.media.AudioAttributes.USAGE_NOTIFICATION import android.media.AudioManager @@ -19,28 +20,50 @@ import android.provider.Settings import androidx.annotation.ChecksSdkIntAtLeast import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.net.toUri import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn +import dev.zacsweers.metro.binding import io.element.android.appconfig.NotificationConfig import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.push.api.notifications.NotificationSoundUpdater import io.element.android.libraries.push.impl.R import io.element.android.services.toolbox.api.strings.StringProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import timber.log.Timber /* ========================================================================================== * IDs for channels * ========================================================================================== */ internal const val SILENT_NOTIFICATION_CHANNEL_ID = "DEFAULT_SILENT_NOTIFICATION_CHANNEL_ID_V2" -internal const val NOISY_NOTIFICATION_CHANNEL_ID = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_V2" +internal const val NOISY_NOTIFICATION_CHANNEL_ID_BASE = "DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_V2" internal const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V3" -internal const val RINGING_CALL_NOTIFICATION_CHANNEL_ID = "RINGING_CALL_NOTIFICATION_CHANNEL_ID" +internal const val RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE = "RINGING_CALL_NOTIFICATION_CHANNEL_ID" + +private fun versionedChannelId(base: String, version: Int): String = + if (version <= 0) base else "${base}_v$version" + +internal fun noisyNotificationChannelId(version: Int): String = + versionedChannelId(NOISY_NOTIFICATION_CHANNEL_ID_BASE, version) + +internal fun ringingCallNotificationChannelId(version: Int): String = + versionedChannelId(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE, version) /** * on devices >= android O, we need to define a channel for each notifications. + * + * Extends [NotificationSoundUpdater] so consumers can re-create the noisy and ringing-call + * channels when the user picks a new sound URI in settings. */ -interface NotificationChannels { +interface NotificationChannels : NotificationSoundUpdater { /** * Get the channel for incoming call. * @param ring true if the device should ring when receiving the call. @@ -66,14 +89,22 @@ interface NotificationChannels { private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O @SingleIn(AppScope::class) -@ContributesBinding(AppScope::class) +@ContributesBinding(AppScope::class, binding = binding()) +@ContributesBinding(AppScope::class, binding = binding()) class DefaultNotificationChannels( private val notificationManager: NotificationManagerCompat, private val stringProvider: StringProvider, @ApplicationContext private val context: Context, private val enterpriseService: EnterpriseService, + private val appPreferencesStore: AppPreferencesStore, ) : NotificationChannels { + @Volatile private var currentNoisyChannelId: String = NOISY_NOTIFICATION_CHANNEL_ID_BASE + @Volatile private var currentRingingCallChannelId: String = RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE + + // Serializes concurrent recreate* calls; readers stay lock-free via @Volatile on the id fields. + private val recreateLock = Any() + init { createNotificationChannels() } @@ -88,9 +119,27 @@ class DefaultNotificationChannels( val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR + // Single-snapshot read; keep this path minimal — extra ContentResolver lookups here would + // risk a cold-start ANR. + val config = runBlocking { + val initial = appPreferencesStore.getNotificationSoundChannelConfig() + // Pre-dialog builds mapped the noisy `SystemDefault` to our bundled message.mp3. + // Now `SystemDefault` means Android's system tone, so untouched legacy state would + // silently flip sound. Promote it to `ElementDefault` once, gated on version==0 so the + // migration is idempotent. + if (initial.messageSoundVersion == 0 && initial.messageSound == NotificationSound.SystemDefault) { + appPreferencesStore.setMessageSoundAndIncrementVersion(NotificationSound.ElementDefault, title = null) + appPreferencesStore.getNotificationSoundChannelConfig() + } else { + initial + } + } + + currentNoisyChannelId = noisyNotificationChannelId(config.messageSoundVersion) + currentRingingCallChannelId = ringingCallNotificationChannelId(config.callRingtoneVersion) + // Migration - the noisy channel was deleted and recreated when sound preference was changed (id was DEFAULT_NOISY_NOTIFICATION_CHANNEL_ID_BASE // + currentTimeMillis). - // Now the sound can only be change directly in system settings, so for app upgrading we are deleting this former channel // Starting from this version the channel will not be dynamic for (channel in notificationManager.notificationChannels) { val channelId = channel.id @@ -111,30 +160,19 @@ class DefaultNotificationChannels( notificationManager.deleteNotificationChannel(channelId) } } + // Drop older versioned channels; only the current one remains. + deleteStaleVersionedChannels(NOISY_NOTIFICATION_CHANNEL_ID_BASE, currentNoisyChannelId) + deleteStaleVersionedChannels(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE, currentRingingCallChannelId) // Default notification importance: shows everywhere, makes noise, but does not visually intrude. + val noisySoundUri = resolveNoisySoundUri(config.messageSound) + grantSoundUriToSystem(noisySoundUri) notificationManager.createNotificationChannel( - NotificationChannelCompat.Builder( - NOISY_NOTIFICATION_CHANNEL_ID, - NotificationManagerCompat.IMPORTANCE_DEFAULT + buildNoisyChannel( + channelId = currentNoisyChannelId, + soundUri = noisySoundUri, + accentColor = accentColor, ) - .setSound( - Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) - // Strangely we have to provide a "//" before the package name - .path("//" + context.packageName + "/" + R.raw.message) - .build(), - AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(USAGE_NOTIFICATION) - .build(), - ) - .setName(stringProvider.getString(R.string.notification_channel_noisy).ifEmpty { "Noisy notifications" }) - .setDescription(stringProvider.getString(R.string.notification_channel_noisy)) - .setVibrationEnabled(true) - .setLightsEnabled(true) - .setLightColor(accentColor) - .build() ) // Low notification importance: shows everywhere, but is not intrusive. @@ -166,40 +204,205 @@ class DefaultNotificationChannels( ) // Register a channel for incoming call notifications which will ring the device when received + val ringingSoundUri = resolveRingingSoundUri(config.callRingtone) + grantSoundUriToSystem(ringingSoundUri) notificationManager.createNotificationChannel( - NotificationChannelCompat.Builder( - RINGING_CALL_NOTIFICATION_CHANNEL_ID, - NotificationManagerCompat.IMPORTANCE_MAX, + buildRingingCallChannel( + channelId = currentRingingCallChannelId, + soundUri = ringingSoundUri, + accentColor = accentColor, ) - .setName(stringProvider.getString(R.string.notification_channel_ringing_calls).ifEmpty { "Ringing calls" }) - .setVibrationEnabled(true) - .setSound( - Settings.System.DEFAULT_RINGTONE_URI, - AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setLegacyStreamType(AudioManager.STREAM_RING) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) - .build() - ) - .setDescription(stringProvider.getString(R.string.notification_channel_ringing_calls)) - .setLightsEnabled(true) - .setLightColor(accentColor) - .build() ) } + private fun buildNoisyChannel(channelId: String, soundUri: Uri?, accentColor: Int): NotificationChannelCompat { + val builder = NotificationChannelCompat.Builder(channelId, NotificationManagerCompat.IMPORTANCE_DEFAULT) + .setName(stringProvider.getString(R.string.notification_channel_noisy).ifEmpty { "Noisy notifications" }) + .setDescription(stringProvider.getString(R.string.notification_channel_noisy)) + .setVibrationEnabled(true) + .setLightsEnabled(true) + .setLightColor(accentColor) + if (soundUri != null) { + builder.setSound( + soundUri, + AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(USAGE_NOTIFICATION) + .build(), + ) + } else { + builder.setSound(null, null) + } + return builder.build() + } + + private fun buildRingingCallChannel(channelId: String, soundUri: Uri?, accentColor: Int): NotificationChannelCompat { + val builder = NotificationChannelCompat.Builder(channelId, NotificationManagerCompat.IMPORTANCE_MAX) + .setName(stringProvider.getString(R.string.notification_channel_ringing_calls).ifEmpty { "Ringing calls" }) + .setDescription(stringProvider.getString(R.string.notification_channel_ringing_calls)) + .setVibrationEnabled(true) + .setLightsEnabled(true) + .setLightColor(accentColor) + if (soundUri != null) { + builder.setSound( + soundUri, + AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setLegacyStreamType(AudioManager.STREAM_RING) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build() + ) + } else { + builder.setSound(null, null) + } + return builder.build() + } + + private fun resolveNoisySoundUri(sound: NotificationSound): Uri? = when (sound) { + NotificationSound.Silent -> null + NotificationSound.SystemDefault -> Settings.System.DEFAULT_NOTIFICATION_URI + NotificationSound.ElementDefault -> bundledMessageSoundUri() + NotificationSound.ElementFade -> bundledFadeSoundUri() + is NotificationSound.Custom -> parseUriOrFallback(sound.uri) { bundledMessageSoundUri() } + } + + private fun resolveRingingSoundUri(sound: NotificationSound): Uri? = when (sound) { + NotificationSound.Silent -> null + // The ringing channel has no bundled tone — treat ElementDefault and ElementFade like SystemDefault here. + NotificationSound.SystemDefault, + NotificationSound.ElementDefault, + NotificationSound.ElementFade -> Settings.System.DEFAULT_RINGTONE_URI + is NotificationSound.Custom -> parseUriOrFallback(sound.uri) { Settings.System.DEFAULT_RINGTONE_URI } + } + + private fun bundledMessageSoundUri(): Uri = + "${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context.packageName}/${R.raw.message}".toUri() + + private fun bundledFadeSoundUri(): Uri = + "${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context.packageName}/${R.raw.element_fade}".toUri() + + /** + * Lets system_server ("android") and SystemUI read our FileProvider sound URI; no-op otherwise. + * SystemUI hosts the lock-screen notification surface on most OEMs, so a missing grant there + * silently mutes ringtones when the device is locked. + */ + private fun grantSoundUriToSystem(uri: Uri?) { + if (uri == null || uri.scheme != ContentResolver.SCHEME_CONTENT) return + for (pkg in arrayOf("android", "com.android.systemui")) { + runCatchingExceptions { + context.grantUriPermission(pkg, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + }.onFailure { Timber.w(it, "grantUriPermission(%s) failed for notification sound", pkg) } + } + } + + /** Parses [uriString], or returns [fallback] (the SystemDefault URI) on failure. */ + private inline fun parseUriOrFallback(uriString: String, fallback: () -> Uri): Uri = + runCatchingExceptions { uriString.toUri() } + .getOrElse { + // Don't pass the throwable: legacy persisted URIs may carry SAF auth tokens. + Timber.w("Failed to parse persisted sound URI; falling back to default cause=%s", it::class.simpleName) + fallback() + } + + private fun deleteStaleVersionedChannels(baseId: String, currentId: String) { + if (!supportNotificationChannels()) return + for (channel in notificationManager.notificationChannels) { + val id = channel.id + // Match either the unversioned base ID or any "${baseId}_v" variant. + val isBaseOrVersioned = id == baseId || id.startsWith("${baseId}_v") + if (isBaseOrVersioned && id != currentId) { + notificationManager.deleteNotificationChannel(id) + } + } + } + override fun getChannelForIncomingCall(ring: Boolean): String { - return if (ring) RINGING_CALL_NOTIFICATION_CHANNEL_ID else CALL_NOTIFICATION_CHANNEL_ID + return if (ring) currentRingingCallChannelId else CALL_NOTIFICATION_CHANNEL_ID } override fun getChannelIdForMessage(sessionId: SessionId, noisy: Boolean): String { return if (noisy) { enterpriseService.getNoisyNotificationChannelId(sessionId) - ?: NOISY_NOTIFICATION_CHANNEL_ID + ?: currentNoisyChannelId } else { SILENT_NOTIFICATION_CHANNEL_ID } } - override fun getChannelIdForTest(): String = NOISY_NOTIFICATION_CHANNEL_ID + override fun getChannelIdForTest(): String = currentNoisyChannelId + + override fun recreateNoisyChannel(sound: NotificationSound, version: Int) { + if (!supportNotificationChannels()) return + synchronized(recreateLock) { + val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR + val newChannelId = noisyNotificationChannelId(version) + val soundUri = resolveNoisySoundUri(sound) + grantSoundUriToSystem(soundUri) + // Create channel before publishing the id: a reader landing between the assignment and + // the create call would notify() against a missing id, which Android silently drops. + notificationManager.createNotificationChannel( + buildNoisyChannel(newChannelId, soundUri, accentColor) + ) + currentNoisyChannelId = newChannelId + deleteStaleVersionedChannels(NOISY_NOTIFICATION_CHANNEL_ID_BASE, newChannelId) + } + } + + override fun recreateRingingCallChannel(sound: NotificationSound, version: Int) { + if (!supportNotificationChannels()) return + synchronized(recreateLock) { + val accentColor = NotificationConfig.NOTIFICATION_ACCENT_COLOR + val newChannelId = ringingCallNotificationChannelId(version) + val soundUri = resolveRingingSoundUri(sound) + grantSoundUriToSystem(soundUri) + // See recreateNoisyChannel: the channel must exist before the id is published. + notificationManager.createNotificationChannel( + buildRingingCallChannel(newChannelId, soundUri, accentColor) + ) + currentRingingCallChannelId = newChannelId + deleteStaleVersionedChannels(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE, newChannelId) + } + } + + override suspend fun readNoisyChannelSound(): NotificationSound? { + return readChannelSound( + channelId = currentNoisyChannelId, + bundledUri = bundledMessageSoundUri(), + bundledFadeUri = bundledFadeSoundUri(), + systemDefaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, + ) + } + + override suspend fun readRingingCallChannelSound(): NotificationSound? { + return readChannelSound( + channelId = currentRingingCallChannelId, + bundledUri = null, + bundledFadeUri = null, + systemDefaultUri = Settings.System.DEFAULT_RINGTONE_URI, + ) + } + + /** + * Classifies the channel's sound URI: null → Silent, bundled → ElementDefault, bundled fade → + * ElementFade, system default → SystemDefault, else → Custom. Both bundled URIs are nullable + * because the ringing channel has no bundled tones. + */ + private suspend fun readChannelSound( + channelId: String, + bundledUri: Uri?, + bundledFadeUri: Uri?, + systemDefaultUri: Uri, + ): NotificationSound? { + if (!supportNotificationChannels()) return null + val channel = withContext(Dispatchers.IO) { + notificationManager.getNotificationChannel(channelId) + } ?: return null + return when (val sound = channel.sound) { + null -> NotificationSound.Silent + bundledUri -> NotificationSound.ElementDefault + bundledFadeUri -> NotificationSound.ElementFade + systemDefaultUri -> NotificationSound.SystemDefault + else -> NotificationSound.Custom(sound.toString()) + } + } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopier.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopier.kt new file mode 100644 index 0000000000..2eeb07189a --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopier.kt @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications.sound + +import android.content.ContentResolver +import android.content.Context +import android.media.RingtoneManager +import android.net.Uri +import android.provider.OpenableColumns +import android.webkit.MimeTypeMap +import androidx.core.content.FileProvider +import androidx.core.net.toUri +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.di.annotations.ApplicationContext +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.CopyResult +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.SoundSlot +import io.element.android.libraries.push.impl.notifications.notificationSoundFileProviderAuthority +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.io.File +import java.io.IOException +import java.io.InputStream + +private const val MAX_BYTES = 5L * 1024 * 1024 +private const val DIRECTORY_NAME = "notification_sounds" +private const val FALLBACK_EXTENSION = "bin" +private const val COPY_BUFFER_SIZE = 8 * 1024 +private const val MAX_DISPLAY_NAME_LENGTH = 256 + +// Defence-in-depth: we only ever expect URIs from the system ringtone picker (content://) or +// the bundled resource (android.resource://). Reject anything else before doing I/O so a future +// caller that constructs a NotificationSound.Custom from an untrusted intent can't trick the +// copier into reading file:// or unrelated providers. +private val ALLOWED_SOURCE_SCHEMES = setOf( + ContentResolver.SCHEME_CONTENT, + ContentResolver.SCHEME_ANDROID_RESOURCE, +) + +@ContributesBinding(AppScope::class) +class DefaultNotificationSoundCopier( + @ApplicationContext private val context: Context, +) : NotificationSoundCopier { + // Per-slot serialization: concurrent picks for the same slot would race on `.tmp`. + private val slotMutexes = SoundSlot.entries.associateWith { Mutex() } + + override suspend fun copyToAppFiles(sourceUriString: String, slot: SoundSlot): CopyResult = withContext(Dispatchers.IO) { + slotMutexes.getValue(slot).withLock { + runCatchingExceptions { performCopy(sourceUriString, slot) } + .getOrElse { ex -> + // Don't pass the throwable to Timber: SAF/MediaProvider exceptions sometimes + // embed the source URI (which can carry short-lived auth tokens). + Timber.w("Notification sound copy failed for slot=%s cause=%s", slot, ex::class.simpleName) + CopyResult.Failure(ex) + } + } + } + + override suspend fun deleteStoredSoundFor(slot: SoundSlot) { + withContext(Dispatchers.IO) { + slotMutexes.getValue(slot).withLock { + val dir = File(context.filesDir, DIRECTORY_NAME) + if (!dir.exists()) return@withLock + val slotBase = slotBaseFor(slot) + dir.listFiles { f -> f.isFile && f.name.startsWith("$slotBase.") } + ?.forEach { stale -> + if (!stale.delete()) { + Timber.w("Could not remove stored slot file %s", stale.name) + } + } + } + } + } + + private fun slotBaseFor(slot: SoundSlot): String = when (slot) { + SoundSlot.Message -> "message_sound" + SoundSlot.Call -> "call_sound" + } + + private fun performCopy(sourceUriString: String, slot: SoundSlot): CopyResult { + val source = sourceUriString.toUri() + if (source.scheme !in ALLOWED_SOURCE_SCHEMES) return CopyResult.UnplayableSource + // Reject unplayable sources up front instead of persisting a known-broken pick. + val sourceRingtone = RingtoneManager.getRingtone(context, source) + ?: return CopyResult.UnplayableSource + // Capture the source's display name before copy — once persisted, the row only sees the + // FileProvider URI, where Ringtone.getTitle() would resolve to our internal filename + // (e.g. "call_sound.ogg"). RingtoneManager → MediaStore TITLE → OpenableColumns covers + // system tones, MediaStore-indexed audio, and SAF-picked files respectively. The value + // crosses our security boundary (foreign provider → persisted state → UI), so cap and + // sanitize it. + val displayName = (sourceRingtone.getTitle(context)?.takeUnless { it.isBlank() } + ?: queryOpenableDisplayName(source)) + ?.let(::sanitizeDisplayName) + + // MIME comes from an arbitrary content provider; refuse extensions outside the platform + // table to keep filenames bounded to notification_sounds/. + val mimeMap = MimeTypeMap.getSingleton() + val candidateExtension = mimeMap.getExtensionFromMimeType(context.contentResolver.getType(source)) + val extension = candidateExtension + ?.takeIf { it.isNotBlank() && mimeMap.hasExtension(it) } + ?: FALLBACK_EXTENSION + + val slotBase = slotBaseFor(slot) + + val dir = File(context.filesDir, DIRECTORY_NAME).apply { mkdirs() } + val tmpFile = File(dir, "$slotBase.tmp").also { if (it.exists()) it.delete() } + val finalFile = File(dir, "$slotBase.$extension") + + val inputStream = context.contentResolver.openInputStream(source) + ?: return CopyResult.UnplayableSource + val bytesCopied = try { + inputStream.use { input -> tmpFile.outputStream().buffered().use { out -> input.copyToCapped(out, MAX_BYTES) } } + } catch (_: SizeLimitExceeded) { + tmpFile.delete() + Timber.w("Notification sound copy aborted: source exceeds %d bytes (slot=%s)", MAX_BYTES, slot) + return CopyResult.FileTooLarge + } + + // The finally below guarantees the temp file doesn't outlive a thrown probe / rename. + var committed = false + try { + // Probe through the FileProvider URI we'll actually persist (catches torn writes + // and OEM codec quirks). + val fileProviderUri = FileProvider.getUriForFile( + context, + context.notificationSoundFileProviderAuthority(), + tmpFile, + ) + if (RingtoneManager.getRingtone(context, fileProviderUri) == null) { + return CopyResult.UnplayableCopy + } + + // Sweep all `.*` so an extension change between picks doesn't leak orphans. + dir.listFiles { f -> f.isFile && f.name.startsWith("$slotBase.") && f.name != tmpFile.name } + ?.forEach { stale -> + if (!stale.delete()) { + Timber.w("Could not remove stale slot file %s", stale.name) + } + } + if (!tmpFile.renameTo(finalFile)) { + return CopyResult.Failure(IOException("Could not rename temp file to ${finalFile.name}")) + } + committed = true + + val finalUri = FileProvider.getUriForFile( + context, + context.notificationSoundFileProviderAuthority(), + finalFile, + ) + Timber.d("Notification sound copied: slot=%s bytes=%d", slot, bytesCopied) + return CopyResult.Success(finalUri.toString(), displayName) + } finally { + if (!committed) tmpFile.delete() + } + } + + private fun queryOpenableDisplayName(uri: Uri): String? { + return runCatchingExceptions { + context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null) + ?.use { cursor -> + if (cursor.moveToFirst() && cursor.columnCount > 0) { + cursor.getString(0)?.takeUnless { it.isBlank() } + } else { + null + } + } + }.getOrNull() + } +} + +internal fun sanitizeDisplayName(raw: String): String? { + // Display name comes from a foreign content provider; strip C0 / DEL control characters and + // cap length so a hostile or malformed value can't bloat persisted state or break UI rendering. + val cleaned = buildString(raw.length.coerceAtMost(MAX_DISPLAY_NAME_LENGTH)) { + for (ch in raw) { + if (length >= MAX_DISPLAY_NAME_LENGTH) break + if (ch.code >= 0x20 && ch.code != 0x7F) append(ch) + } + }.trim() + return cleaned.takeUnless { it.isEmpty() } +} + +private class SizeLimitExceeded : IOException("Sound source exceeds the configured size limit") + +private fun InputStream.copyToCapped(out: java.io.OutputStream, maxBytes: Long): Long { + val buffer = ByteArray(COPY_BUFFER_SIZE) + var totalBytes = 0L + while (true) { + val read = read(buffer) + if (read == -1) break + totalBytes += read + if (totalBytes > maxBytes) throw SizeLimitExceeded() + out.write(buffer, 0, read) + } + out.flush() + return totalBytes +} diff --git a/libraries/push/impl/src/main/res/raw/element_fade.mp3 b/libraries/push/impl/src/main/res/raw/element_fade.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..bf31420e495ed94ffcf596a9cc229221476ff629 GIT binary patch literal 33645 zcmZshbyQSO{P*t$unSAq65_Iiv`b2NcMAy8-KAi$OLv2GcSxgv%F>OrfGCZmf-MO9 z)9>$j&UyZL=8t=4&YUx6?(4qi{kikGqopbe1^yKdQxg-lzjsOifau!aa}k#k5|$7W z#$f*I{of1W!u0=X{r{_4J}$n0d;Sgs+y;OMMF2`dN=`*fPmg3_W9PiV$0vvpm5`E= zmse8N(AG0FG%>fdwsmlFa`o``3%nN^`Y0?sDmFeoDK#TIx1hMVtg@#5SxZ}6XV=T# z{-L4KiRsyew<{}a>s!0~N1r~OefxQSN%%LDzjJo{H)qLzjsIIXA>994jZid9p;sbY zWI6$G@VV`4cr>$!P)Q8~bQnx>Oi$6Ch6OIcTiod=07faJAtDL8E?{@nf`y{O;qUh& z<4S-eeuB-~Pwh{H@;rtt!c|X~;jOJ3-@hpbsp|gjDV`Ayz3A!bMswYddV7D1qqi*h z+s$W}A+h;Iu~EOzV3(7KyBl^srL z1m+kmNg(h9E`80VY#R?1N#=f|e!S0m<}ts;&K&XfWu5^Tca%4?)$as}W0ig3_AwBa%$edzpQyG^rvF+_nG&kgI5GXejbiEzPY_xNFStmub0^ zU9%|-06Ai}1XI!QJgk2pcgOo-P2iC9Ep_jZHqPl8yKe^R_GL{IXJ$W*<>x>DI)0*X zJ!Pc){SFagOpRZc#ILzyY5)ey2Sp6# zH|=GVq7_IEmA4T_)pG0RhM{+UZ~gAtx+v1(G}igEc9z?b6SgO5)HJbk^#*ypqUf^zzEEEi<@`jhx?Welm3$%WYUyB zSYG#`lc(3CH=^J&epp|n0n46LA1`dkqb~gIr=K)pmIz-}hw})PqtOa~lq3zuqtFHQ z?7T+JZmfei-5tAi%0$)3#dL$wZ=--tSr7d;b8)7dl4zZDF5J|R>@U>7ZwkN1Ax$Ot zIHqX2keZ;tI}Fpe>Rk_4hQE7{%;?tCnK#k}-ZO7(5#67))Y6X47}w2at~BF1sIzFO zbXGfBtUB)CTa=;Ys`R+0eT+gW^HfV@Xp7JnrUTILPLG*KKMM@JbvG*3k$8|5PiXoR zLujkN`h3`-2#-1m5o55nqDDgPFzODEr2`dT+vOmgq5t@kL+P&R8pw3V`w4}=yCw_C zNVz5~{b}ze@I2}`=oB6;aGxsKRS}7rWJ>RxZ-g!K!MfiWn^@LkS|=IlR6})a1YSiI zoGjGp{9Ij(iR9N^syH9pVXj-Q;&trL!Mva6&&xwS`bt(tZ@(lvGTTTOYu}pNnCAZy z$Efjn!DOtM@V($2;f@Zp_-+M=rbLSiAd5zzEcT{0#NUKT}JqOwyEIU?dC4wccL3tTqz#Q$}LdB54be9yQ-=J&u?c9;1 z^03OMj>h$W{6mO|mPjE{>z7uUA9V!A!j~dIEMlF%1zqmdM8}=6kJ2~>&D_jk$z^ib zFS7g0u(sl<#LYgE@4S63YH*cpK_KK2E+3^ggLvrRPJKKIbA>pckB+#j1?=^~tW(i& zf`#cRl{JW#_CayKfsdsVI|^*D1cj~Gx6~Q~I4r9j0~t5D_LW@`)gDYq%u#iwzO75k@IDz(2|KS;mbPVZJ`9g21}x22Vw;vGbYCmlo{13@eWL` zQ3w<0=PMS8&yVN-gl*o%O{?(RI4fz;v#tgK%?&Lhf7%iogK`+?a&W47<_tM@Y)l&0 zh+{lUIcHqNF7af?Fri9U?#f?}1{}!}PMEFA4(}RYq_7Uv6_ZMC(0`Yj@G*9IAP^_% zT=V*dzFkkbo+p}Ft#8riyyC6xngjKcWLU34dWtP!Njof^;DrI&;&`M4n*kAMn4j_W z)`kaDiw&Z&r!5v+;_mjNcW$`VtDNfPsof25!9R3JRG~nVj#IKD|{s&Ue#**dUG^%Q+jCw0I2d^7`4Mce|2;bkd*VuP$8%cD&u zKER_kW>FsQ<~fbGzlv{*v3m3B5C|Ht2tA)eS@@WGhPZA$++a7;qM(Tq!0;fzSYm7P z8+DP9gP_b`q5F3B;dodi#M2GzN;B^#GOVYVJIt25R&`?n$ZRgKiMG*{f-0GlToPA~yvR^-ZcRguq51 zjP=e^0VPyAzetmzL}AbgYkwN8yGmN!{aAd0N#J_OWk;N+`^W`+ zJ$Uh-a_WMk;cWYLj(6z+M=EM&<&y6*Op(r!J>r0$%mdV2K6GuM0db*I`6u0VuRXw^^F>YcY~mPK3Mt*t*sR^+#vWz})(wcNh=v@!Rg(swnJ~ zmj`^e8g2;wlA=}4wAhP?1nZtZv(#fya?4E`O^JTCP(81tYi@x{ecj&SCVlD2hQ8+_ z>2ADIY{riNK{%gSumE&lSZBk4cNJEzE0t`+8O4bUsiP)*mngvwgEllz1b^PXMtR4W z&cz~l%IeXA#zQTd-_faV`vYNLzRBkRR0;y19>E?@Xso(IJG2l%!6K68cZHu3*3x5Y z-OQF3#wuwAzMN%}s1=gshM?PEL<{FuxnG;a!k*WUmkRHUtsnhf6LY+GbU7EzW+6%J zrpJ1OJk(wjM3=?fUJ|*&VOLF>s$Q%UuEb}6qvp=WW)LWisYvx}7ujgD#%{JU+pi&F z7JJ{XZ5H+tQ7ed=FIn>I+ca6|nXRN^?$u4?<7}>uXYs&Y;!z|p?n4A%0czyKc%x!~ zW}ph4FzE7M{6#>vRmNynjZZ~!-Pas-F_dcLXajE@{1R35K{@=rCOOJpo{d<`Pi1B+ z@zjrc8%3g8Z>e+jTdSN@_csx2e3uTKC3Vj}mUJ*cl%>(~_gX<;K}>D+!P%>T<|5PI z!quiw(^!R6-g(>D-87ZDrxkG-tB?Wphu5CZ)7q{RlwOHpK`co++ zNj1sl0{zw=P<)+*Or%GwSXx@jWuOT}9Pz};+;E^+6#+1l3v?^m@sNPdaLxQk(GH_YcO5X7Kc@|U z4?FsMgdVj#{FQ?`-lMYkV#8OhYVh_PAr(+B)oBM-E)QYG6s3+bOJR7>j+gu}LX)=vl-GK4D+ARP;h z6+2Lm!!C%IVB3UeCC*SUuJseYjh~h`&na*n{Dr@;8-JmnrmWvlr~#?$-%cwXMR5UO zcF;=$H#dB9!LaC~@vxxJ?9D^{2*mf|Zg@^VDuzyNoe3Nn9<|W~hvG-K^nC;eO$#Y! zh_IkZ5uzG4hO6}u+KX8Kjj2t)LHpY(I#f#Y69{=jvEkRIJF7aU;OL#Ah2lp) zkGVi3RxXM2cZ_YdsvGd<01SnN@ub6Hj#yKk$Y6$!^5?95|M-()A-QPtY4;64D&nYc ztOH2tBT|1q4q&vfJWb>cm#|2c*i1n{k({N`z zEf&U{i44i~ZW-N)y(zqrvF0Rgadr&=!oB$QOF;d23k&cBauIy#>4 z4Xz#?Sr4az(Mv(0ZiP~g=1Jt0I9RpMItviIwbf`^bt>YVyVtxYo@%*% zOW03SExr`fG9XBs`+wK`!egv7sY)XBRHxr>ZgBih5da{KCY-ULkQJ$2y(z19v&TG1 zw?>T|R(l#o@7;zZLmE6x{Ob;TOL* zho!OPthaS$)bcZTy-#hOtQ;-}TiGRQG};!@cSbt%=AdG!?b3m>OfT%h(Q0IlZiXRT^frmk~+&Unh7f$35_1uJ{ z+&}L7Zl99Bv7yRvSm)GBULMk)XU`eEbe}D$7fCA!czs2)Ys-9;Q10J+x6n22o+T#f zjxIELDxpQl*>UbYPga{g$+?INjFzmzG4QtD zka}<9@-T1lPFVTX57p<&Z-`~yVgK$ejSOKK&L(YO2!#61JR*vg^TdP}`xVSeluSYf zr7y!Z(v%Ip)+|f=$DaWTkykV@>5=*&f?iFuN9!c=f~54lKH}fTdy4%)WvBP`DXGY6 z%5+v6KO4Tq>#eg$-@tnfwfExN-K{Da;fHa_rfZG2csR#uA74xm+RNc93f}~2bJw@v zDOLaoM$=Bk5-f6$$`9vQ>PiO8s6?J~n`*Ks$awbrBKkQNKd`IbSoVV6!k6e-`HSRl#()y%ypIIX z=qE-TTNnvG4GmFrycvpb7)WG4u@a;?( zYYDquA26cy#6fGMN7EI6u!2bxl~1*e<~e?%z1jL;YIa)A%oBl2n1!nE5IsV$vyYXW z`6vHjHVaeCptBAm_d6oR_B^&>1>LZ!Zmly<+D$lBdv~U~!COl2R(*8|$Ipt&9si*XD=)f1fIG*t8$pNK+ddcAu6Fw@pJb{9e zeZ$xJ>EUA3sj7ch8ut*JVc^8QX93Jp23FK`eLWQQu_N?ZUpYRR9zdw(C#Y=>1)lTV z*J2LKl;yE`(#w?o+gcG`Vx_N|3(wj(&`*3W`d!_2dD;jbZk=i*OhC{j?N+pWTsHi3 z^O+zv`N;<|59HF}C@@Aey3>Mf+*(tc&HRCAwlDeOPmyTIEM37ri2Knf!jG3I)k)m2 zVf6M-U9^WF4rb%ua_M;1{=p1Y(F*+#pG$mg9z2lD?y>Mnsp@USClnt#`Q^3Sw)1>r zAr%4d&eqSX;~swpV5);TegG11GbTM%Xtxlt67v2a|Ft^RsP|(2aGo~B_SrxFY5=XM zl$qzvJJupwtFPoDZYCP;>Lke?oleE^2FFygy3@LK6;VN%uU!U(_~%;j0f3^fEq2r> zt*3I6wbfMPu3szoa~e(AgN)5TCsczro43HX;g2`H)Cb2;e&jiD3*}}R-I;qPcfj1P zpyL}7G_Kv2Vf2xDu5JtaWGJUAS^4n-Yu&tf3l*M$Deqz>B}+OLL?V`dPm59ug&A%V zi;rfcVURVLL{nQA=9cO9YtuZKiA@4Ohd-`HN$f)O-ha|z&aA_NiGcT6WZKH^Bg0D3 z5x9guNZ>jKNFibC8^#(9fv#^k4cT=*LDRfc*M)B0;iab-pnPE{U^y4DoA$i4>Ejo# z>iU(uY+GnUmKEj0JBmkTjjK(9uRh+{d4Bb!ckJ(~0nhsw0-AdFtJ0g9KA!t|vr!ws zzt{;^(Ek`yZSz=D(j(Ru4S84G^V_y@Rd(nPRdIfq%^Co}0Q@^F$!=-K>}U-@n9Q}KTLNwz8BCFUbuI*nu3L83|S79Uogy&Y|<_c?zh+&DQkqzT_romJw5~ z=R`v1v?>ZqfLB~^XEu*X_7hAsD-Xl1`jrRyZKAj4?`UwKP?9bjl3q`5_LUdyM^QrT zOpb}h6XM3E#Eq~>RN1=#fRhqjY1Ch5<|AUX+9PXwHfUl&zN9hTl=V)P`=M3f+4biL zgN;j3Wk1^lYkk==36~73RVfobkDF%W4!{{#x%AE=8Zj3r2 z+}3@6MiOrJ*a$9vvK8-5BCsbvln;*L=Mp+N`afa zITPHWO{T=ORM81csP0#aB}TFy_Xhe%Y91POdH9a9_i)vXfSa}RO2Ws3N@Le6hDJjaup4L#P2)1ySJOQAoNq7u>q#JpBx$wpC0BWEg zHS3USN%5SfQX3{0A5bLVZ{+fLv%jP}8zd7w=s8RJ@yVFPdf4(aP0ktbaxjf0r7XuYl=GVR#I%YB)!RB37QI5V5%#<9 zh2WF!K!ULx?yYMXV`X#cg~2r?0;UZ(gW6SY&N7&dnpOqViX7{+7Ow0_|LlMKqX3#I zkv9`wrn*wfhmCR^YH>47dJQdp{>Y5l?=eZmmeG>e*_{S8ng{v=-`MHiuBr~#S-r13 z@WtuQ!T?bNKxWw^*bG8C_^ZB!Wn2*pmA|ZCcGA@h5^5qXx-%wt(3x^XoIBM}*ZFS8 zHIx<3x!2b6yyf_didC|RY8^eSs43$SGvS*(?sFLcq}2mczTcB_?J#1o0oJdTiP3h! zINNVES#Q5G9Rz&I8Vxy6<)y8ozwb{$n(MUac8v$45okHr_~YKw8h?xB$=D0$7?lj3 zKc~j@Say#aENR$lA3=>Gm{~{8WO=<0@6>JD$83Erx6bwI3gcZoDU44AyfrF*{^-_o zW}4uWC=tx7_|Q0RmQ`aNTreocCvl$mht=e2V5$TFRILHZy2me$QJ-?@9=X)``AhcK zE8>_iyb5WTo>Fs2sT&Rx_BJ~8UtWd{D>dTUl5#@y!{s2j>`oD72j{WyqOV4i3Gc)mn-yNspv@_v0*s zDAgyTn_+6g`~uDw#&go-8~bZNurxgyl#8fLlk@nw_PNB;!=mOWm=vse;zois<_-*Y zBG!ZN-KgA~RIWmXbMvHc=7hE?64`@MK@jTTmDR^`h)7HArtdjxrX@_bmTM;vY*(WL z+T_3VOV#l8cRsSIFS*!gV}S%Mo}C(y^73;c7VSkpN&Sn{KvloQ1%pA^hB6UYHh7N@I|P@j+;~j zh4uUe5M1!JT+D^8PIqB=3vS)s-Ws5HL18)*9qcdyFGtPTkwCwwiF!ON6%+2oRyHg< z?br|xeu1_~hQC*!s$lSYb4@;2C)c(&MKBQVgs8U=IwZ3-W(2siW&oIuBHe;JosG#g ze^I4cEtTYFdcpb&E8TNR^HWj=3&AO+5dNDO-MmehP@^s`wux@m&_zBXso0x~ZQ*Z@ z@CDY^r}By~_s|qTG(*S~ciU0K!W{G96!H0)5yQrVu%Z1u<%s!qniYO85_e0-WrC)c zbjVaOL+{kUbIjwXaX;E91!CN1Z3cw$sWKKe{9`CsZr}j`-B?1D{xVfLCOY&SbWip} zxL}UOh%RYFUosC;Kz9EbnyS{KIdZJRSk_CwwMd6Ut7mnqfBt^q%>er))^!_$Fae{& z2cJL2Y6f~NuKWJLpD_LSJ1Qu^5TfQ1!LFzrk>Y?H5FO0mS2;%QT>@$3K6B zuzI8&z7pt|LI*c&Q4r)jJrkth-hJM}hS?vW_9gazfY-4voXyg|d`a^#)hA0`>A~3B zQm5|>#t&Z@zD-o{@1WVdhqT*n9cmgwosIzAP+;k;W z9d^?`zCL1Nk4S6yO^f2joDvf47jL?yEn8cnHe6hutUtOfH&ox{_Kj_kZ@r4fTUA)& z=AE65HdqV0W@4k+ePZWuwiebE`f?XidAJm(($Bu zAlB@Kt4r!cpEBUj_%@w9ETw(3n^cX9d=^^ke?PvsdoQh+NYZ+4H_Z1>9hM4UxWbb% zZv5Q~!u1FJ`KVKZg!>)PT-0k~sqTAjWu0xKTV|5m6{)o!v$frw`OiD#Vz?=L-orCK z;fal^X)Zf|PR0hU4KQ)Uju5N5_|Uobj>g_{dI*4C0O8Ag?m4iyKkXoP#H96r_T1SF#_Q01|o7AgGaZ1kf%!+DP?`0(~Ujy!^rLrrC zo9*{e`T*^j-^TLO?#UX;$T-!b44S%~a*jD=l1emA6+3u`&KU=4t4C0V?lNYvjuSdvpdz43UJo5M#TPr$!svqO-D zU-5U`E!*|a#X}V;e7*zsKe&*5X%l@Tdn-89&Y*-CD6*(0l|(Y;k!mBR_rywOX3rqk zi6#;;R8Avfr9|KujE>-HZjOvASgeM|j7P(~7(9^IVJ6D!?i7*9lP%!SF^tHxp|!|M zxbRtRIw|eA!J!i4u5X6@oBxo*le!$Hk3D!2Kdj2&0I<`9_b?N^r;bM&^^*w&v%0)U z=Uq^>XB8KYD1i=*7bUmzng$<9I6v@S3)2vKM!{COJMtk)c!F$->&v^mEnnH60ayqM z&@g$CNz5SxXPo5e7UgUh-EIRU)5IP_%*FokKaA`j|I0s5$?tafZxvr&ZPMi?VSRV5 zF^5dfxXDt{kV#cyN%y;PN1FUfPBKSZmK|H$f)+yj{PmncuG~3ifof)%Xk{;rXAh%3 z7@t>2cyMMYl`~F=LofhD)RGQ#PiG-ShO>IF?StKx;`?weT|eW2l6Zj`S|t(__v#x` zlGJsQDer=usd;nd*ylQ&xy60XzA&boo0m2YjZ3WS8{I4Lg6$?(oj-eBFO@@yRpNzV zNWl?F)BYF_%^mXyz9ik#V;XUmhs0K%kx)8S_j}hnP&~|*a2bUC%`{D#vpG>i1lP>@*d&v)N<{M7w=+SEp?8_+8HUHcH!j(97pi$4^#K8`VWxw2=GUH16l z3QN3eB1KPvN*Qc4wPJ@E4v`Soq}`2&&_U)#HcnsUAAc2!xvSVL#4#bsz-Y=BS~-fn zpR5G&t$QTPvUQK?vG@4LgYlwQ^*u!+YfotszheFdL#GXp@*3T!qTP|ufBv&i)Zg}( zf2bwid`C(p5T;9-*mP5G*EPHmg@v+H#1z(YT$N}vnD0z2zO{{Dyoq)0t zbevdu*_(d#Ea@%JrVx>hVUSbp^1YVWS_YM=LfT|W){s|w9zZjRl7=pn7z7^c`gj%- z#UA~#>uD)Gvc^Alu;yEvyk1bw($EeKD~Qe&L2r%gO#jc)Tzc1uy~N?RFmEHMa>O zvNSHWWi$Gp{ty1{fB8o<^0iJ4)I#Ikx7|ozwuqc;!qZsDs@yx7VPN)LmxI!hxi^Ao zPpR%BUcj^abaEwh>3S*hM^9go4>6ewcF)yRd~Fn05AmcqXzw1Hc;&hF#{0p=I&a%O zm+$dISr&^5h5%U!aKi53jHIH>=OjlK;ET-;(bkI0fJ zZKq6qZ*VuWeOLWU&6SDKlJ(=4+Sy{ATH1&?eIf7Gi=wX-Lg*Yq(w?=M$aTS9Jslf6 z;#n;zE&#^+Cx~g@z=b`arwsk_&DLWyi1q|DLoIBvf4|GMM|IV+we@~i_%T_&)w1tX z7niKMQ8BFL4yo{>85SOe@-dO~kf#TjjxB~8zT#MCtUpo8T zh}p0+UP{VyL3y%!t&V2chz}fVQIt5md8^tHM4ES^|Iy9xInO6gwgp!0X|ek7c%$Ac zLK8tb>_^pK@n`)4Q1P>ApcvZ0NESo^E_L+I7bf z)6{lEz&pRBB`C&-ag9G7o|V=iX$oqJMpNDMh{8{;JlV~IV=?h>JW9z#YB=2a^fqwZ9R5I9HPW$P{Bd#b15I4ZuyyyrK} zZ+tGhCA~80QoGCN-ZN|{RwK{Ccn!{DqyHn=*Sq6mnDd0-TTQKsABD+R&))YEjNUwN z3Y$~v#6k!Be8NRk*kwqsv-mUvFc<}E^AV3WWO>qNxcRQJ1&`g6C!rKj6@IH*CRFBD zsI9NNMD4eK?-D!F_!Oy`{(8O^bxfB%97j)G0{3{K=pN249M1W|NLDoBiEz83NTatX z9iMP|5(G2mXq@%3SDlAF3Lz%R`#4OVYNAjuK-KL|h+rINLDUSgj%Zcjk7I19o!yJt zEE%_Tt6}e?$G3(S0~1XMjV=#_mH}ZbcvVrSl@6lS=x@9+wLCbuku#DB{Nulm7@*+N zk;yR*(dO)mcXbmY!)FQj*t?esX1I`dGOZ1wUTU!S0%pRT30{put-^E3h7T@&4Ux zcOzwoU1GOXVn<2M`YD3 z_!FHjE%lP)UAuYe`ZtU9wm!VqIZH`e?ON)&Gqq$rqxTef`}+ppxW49B8H1&@Eu@hn zz$K4}Hk||ZceXvWD=P0`uOTI zwZqr%KPuwFjp}x045|+vM=463g$qYEZ?+g20*cgSp09p{CKXnYci#)U&rs{O%$#J5 zHP*hx+5Y#X{NsN_PD{!4PydEy=-;&WdA0Q(6*B7|9K>Fw*YG|q`K(AnHaJZukI3{M z^K#b{{`9;7=@Z|HN?-!Ss6 zR{?a}2`}(HnyA2=7>p8*MPeeE`)jQ>@+lT>XkVo6%CC~{RtWI4}>8fG<);D0#9i9ck602%6C0j1Q<1jNwI$T#)9U+$n$`t+rjeH6kt1a;MXY%h$ z#4U^{qgwck_;$m&+og}aYDw$r1^nWlw~r3IcL{N$6DLx>(V%?VC6jJq8PHZTqA(Kj z;qoy0$tRhQ3NJ73ot*Bo-&?oJTABf&P5<%#m`JO`rB1eLWvAw}YGWTBmt>m7tp89| zi6oJGx4PxJ-YNh=W|SmE=5k_*C_)fN8V{9xU`N57J>!B^Q_^bywMc7_JBs;@XvZhk z3?17h_Cv8JOAj-FV$^G(YX8%1quUt^U}`(ykoW674% zyKm&Chm<@6)A3&yo31X#KE9kkaqD@K1!F-oiim>x=e4Q7Qd+90zK9?r#vjbYD7xVa zXmgA(FE)58Sh;(`OUA}QQ2v|YEcefQ_CYfGuApuefB%4xh=BMj^J`kjj*6V@$F3#; z*BOEwlYTeGQUdC?OT7@ZWJ&9eiH{8?+o}dl~9(>h=G_|2~&mhWt#4 zVuiM#{l45}!BeDxbL}W0h17QkBTwa&AGqXi7qr_?en>(n42#_Y)_OV zL1Tvq3cHIdeJWm<8{{FI4joPS^sIpSwwJ)mkn!E@so|4`-*=%ZH7!53w@d;*sqFFn zn2xz`+|hy|?ZA4k6mzHHK&8Fv2k}%)F<)!bvGBN0xOi0DdaQ22V3LeZ1Q;AwAI0Pb zvOg$c+{3l^SMUMMLY@cTX#~R1Jz_2S=6=1%2iKn;iH&>+Ed1E35q^R5ipbhJSAE zrcNd;>+lWZ-DIJj%>3QrKdskP@Da?^*9C|TWjQ<>2?m5?jvoX<1;Z&odhb2Q2-X0` z$)oDFUh(hzo9Yqy+$$FKgr3knOU@Tt_jR;Mk7Jni-QD9{DLJ&>%P~ba0e|R%LQ6Ju zZ!m1H9<^)GR@!x13EuAGp!@a|L_NbQLTuS|9R%z%xln5v&M&g5z~4r?#m6v)s=f51X-6aWq)fEMPgA{TCtI&Ih?ZTP4E|KhJ^eh%}gHdIT+mq%7tHIP~8p{mi% zgkADynA#!oT7!p7!ja52Wu(jz#5CZnt!Q59s*|2w2mU%vwTg;y4tMfOG0z^k`h+o? zOYc&g*Kq_#(VOl!J&IzW=b4HS22`Hp1E&?GL@WCG}K6bSp@IwlK(jeESJULBdPNnNN>Je{%E(S(+9irPkxKgu@JhiYF9` zSRRx}gA{SLi?K1gf6#y~MULs{5Iiv^2Hs-f7@4YM&k4i>d`JcOy`U{@P}4R+Tt^%j zuO!9zZPr`Xa*7V~biI)udnkFc4hxaOPh@4&p23lOW*QhKty57ld7) zrXSecuqXAU-9RKc>`Ec^5s6v&@HKTVuRmzby38_C@hLJ(+hqZ>GxTv( zC^a2VkW+57^PdqeHtJai=}7%OtH546A|xPTnyKXnNcQck5J^unbRe>??-tY}&k{L> zKu@Wi2WTI`Vzf#E$CjV1&{FyZxacPuz1Q-y)-B!n!80T;DOL01qukTVLir3Po-J## zkj>EjXZ}c%rU)=r82QArPuIR?+FdN%L9-uaZnl9_3J17&0xTaE4$UMbVEHH%51+p+ z9g|Ip7Po4VGJQo|y_=dQIxUYur<=OQ)M?AR3Vf-aC{l!Uyj}T2eG20`)uTdDxM``| zVYS`81njUDjNPvTIy|#OD#)Sul)c=g z+47(N942n{v1{h%DSfd}iQT&IF0H*0!{+0BBhEaTW9&VX@E<;;p#=?i!gzd6YQ?rz z_-?2Bb;gQNH||RFFIDOD&Z0s~GTOi*jY&a+(@W&wZ7mf;H-nCb8n7b!H)#ex^_x*t z5Zd9kF@I4cQ3Q9cA`RBNkJ>}maib;_t7KosPC-Qqci~p!cu&7Af`1K+ zZGPO(==XhVMr=5N&|=TGPu365E6+Ui*+Tr<)?C$hfK$j}F&<0T*Qz-peB38mv)r&T zordg(q6SKG458I}W!|qR)@9@WNR134ddNijn=K{t^6lx}2*Jg71#=w4JZ%*Gdah~`Dai+U@QXCKV zbTgkiDa1QoO^GXK8U;60IsLX~ZmMfauI22Txh(HfD!2x?X>s6BV7R4oN0@H~%|1X@ z|6L|Q)v;L#HyLwVD%Ve+-t(XS4-x&ZnMN|Ywe2jRwUGNN`Upe}^6&R2$c54|?!7P% zlLgvR2?{5DjKeHltL2>t(sT)k6tPl(_xCMEr(eI3B=cZ+?yLeY z0Si~dq2NwrRF3@aKXDxM7G|+4*=r^OLQH&Cc8z>kP=0!B- z1@zV(SCL~0Vnaz>A4mA%B)?Hh!w|gwmEAmD5YJpBZR?)J=k^R{kj=+y^?*#29p~JT zDf-@zV|_k%a=D+|0yP7l9aSqh)D*$L`Oq7r@i0aphsxTMW54{@`EQ5-6ycI0w!gMe z!cN>xnTDdT<(||45yju7Mcax0dbRh92O93uo_FdQAo(sp7-$9(3i_Kud^1 zWp`hh7c|-tKWQzNJDb*?&?rGpzT z_0+-}QP!hv8Lg*8wW(kAOGZ0Uvvz%^Z|w9)D9a%oKj&nb_{mvt;}e|w)mNIL^Yu#6 zxSMdDdrBrJ80fUlpL$&~#3Y%DE$k!f`a5PIVtuH`YqSRw_h7uZsl_9>gfYeOlsamNP^)UK+2gS!JrX zONpx}*k;H$WxwDyiAjDLlBkFLdvvsc7~n|6LHS%fxW>ZSN8GgY+8tco;MqzebjbaG z{4(Rwec(J-0=)pOYG4sakxC!wZG? zg!S8tM}`uo;(dxpC~aPzA;exD|!{l{$2a@Wr8C zWt_y&v0a0N0|UBHj@uvNCdxA6wYX3G2>MqG?9S5Ll)PX?5?m#KW#4oY*hW6dDxBJ2 zCT2rga5Axx0TIZTpJ zZVZKAo7o!pe#Wo+_D*}6^P65}g6qEU_pqj8;Zj0R%ToXk2~ghxc+{2hYPfUMt(ZhJ zbFf4>qSDRN5lx1iBjSJh-;129VjQNuHd>;O-v1J)t~<7DdsDCSbW@Guul|{YOB!BM z!Ts>%T(epG8Z%6av%eYOH8qujArVl;t~Q6ttrQWWLA&w5ovjLHUIxeLq=f~sC9-4- z8kW*wr{T~ojg9?-$bJ`N2y&8%jM=zA>Bb~tNs}m+h?7n8i{USrTVO`ij&A(-z7HeR zEKHXjT{SnPy*|7?(u`3;d5z`g|G1numw8UOw7sf`FdqGSkB(Z8oED}DaYW-sEC!ut za^RE_-mnTDxRn76x6a4T4l7`1nxp5ezI*!@pZKq=l@^%@P1SMCJF;_Ys)b6)?t85_ zJ|K8l-Vy>h^6HaClSl;AMb;kY zXUTqplB$ur+sAvFjEg#S396BFP-|^SXB@kH_QuRMb(^`Zw;8-{9}4@`R`ulj)sTD7 z{TT>A**0CN_KH_EhfC;zxKkX5s)24Q}eg1 zcP24+8Q(W2;!FE^z%d3a50^0Lq9Qo^PpA+?IAE;~ILFe_!icD7nfIlc`ZIPA6i*%c z9frv)9S2KDU;~;Q{I0@tr2=A61s7{ONov*RMTG3f*8PDGr?wIe&ab_G*V}UuQs*Bw zKU>4-2vEqWVZ}Xf8D;P`DsURdvNP^kCDlHd*Ce0(T1-q&?AKo*b$X5NSv!No#LEQmR% zMH8McUk3Lw4_v!QyZrEZLom<%5}HM;r>E#q2L(xT4T!v#`>1(L`%nL$ z5(gMj>KR>DcDj(!{y;8W#G^0<18-NH0Obz(%f!=WW;rcJj>+xoBSz_^R;#&>I6r>< z$tRQOz4hCF!}Ni5MBuK?Sn2wS-5%sVf=Wd*Xm_H&AIzaw+>6t{Ho9#cK{b)|CrLBt4e z3XVMo)#}1`qapT!*$2&KwN3AUD9kg_uxlsQ+WQ={!cI`F-I5u%}*!_!lfa1 z|3ZOq6(ynwD-SdWYGFe!X(0lXJz^5zl7XA20mtJs5HT8|5Y=+yyQK@H;-r&G3|8Tc zBJ4_xj+CuC!9pvkV#p4jC!8?RfU{S^A#g#Ko=BpSY^F1%me<%|0HpM1*t~XIee{>hd4^t zFSb-MgOtNb14TtauF*P`gHd@0M}wZgvApPcx8#*d8Xw&a2)Z&dZ+dOC-+XfS=Tcbf z5Pg!hAmL9+(`69h#Pw>E4{YTDwX?5nI#j3BX2k`#D)uGkyHJpTBPA6F^N=}>?2mJ? zvW9y%_tHpxId9H}-W_?Cv(qX^W@d!Wip*yl9_{=T_GF#s#n+s4a-IFZkA%DMw#Fi^ z5htojOaf5yMx3r9MCm#OtXwGZA(aw`lX#B|;u>qIR~hE#^H9i&JQ(~zz3|r(BYrOK zW3~Cc-&;p!uaxEZf5Z~ad+A+f0ji4N+n|p0z87B^+K7{({khy#QllL++nSWZF_frp z?HV}tA&U5tLDvlU!_m9h@ta^k!RYCCSk4WyNr_lL72l<(A0OVmxuCK=cxZJ#O|@J{ z`y?$~dQ=G&ZYULNhox;M;EApaN09H^JK@uv0bI~O{vXNb4*&X3o|T=o%4q3*cUe*k zU!=Z$oRSoxTv6!5O6Pt43xa8EYiIVJh`tAD2?wQ-S>(3CM@>rzN z%zAf{(~Sse?Rn@cydJhyX3YaBMX6e06!xspqSG|g*@jibV|!( zc{2R(YW_SSi|qsll1jeqxT&PSdyj0G4^Ultw=EP23N1iNgt6GlH zTW4NAgrktc!E9b4the^%gZE_<@}j<9Y*l){Ken5z`L>r)qC7j9RfWJ5R~dRJ8O0jf zRpWpO7L^gsV0pQl8sC0v`SxU(JGdS5^mu50r*|8`>xo#?1FB|GavHI0l) zD_MIew8$#kyL#-PNWnl!T=2l1R{n}?x9fzK9!(-~LvNt-*2T{-*Y77+2@2 z4qtNsWoB4c8j(xt*Ju@@VX~sC>!3ZiW!_l4N-Gp2Y@z0z-Wx8F!XC`s{6UU%E)J^DT0{A~~M>!|&vN!+5j<_4!2 zC_h{enHQedVU%M`7oiLh2632`oh7H>MeOka+fkHS5~vprhHIFV#*_VK3vA{i zq>R6jtC{dD-?L{cZ|wK=SBUr`t=Cm+n25-W<#0q&Z9i91f#2z;(H14LIbBsz$|Y}^ zGPwVzyR+bGvy0kv@C1TeaHoMH!M#9mcZ$2azECI>cPQ>q+@UzdiaQjS0xj;^Vg(8r zI^V4M3$xbb4?M}W?q}zmE%&Biq!e}4w1S?(N=dX?t|~2PUiwd=pY#pwE`KH%o(yA# zGD|K+$XMC2pCWEPJRvR+vrnS{4gh0aZ#@Ym0c99YFg77|EdzeP6v^ee3f4dVzvJd# zQ%c9S`>DU(f%JPaD}uXhU-W<2$M=x|v|b1;rl#FMx7G0~29wZX_U3#^G-u1^H;6z< zdK3F?32CS$@|Kj-kJR*ocU22Hh=c?$M<(FYi}G&}2z>&elwgK00hDr7CX*RNlNkXh zZlcD#!U*P3EE#Qr0kG>YgDc2MaS)hT=yMXc4jn(foOVI&GrCm0!6Fig&5&vbWHN4s z=1tVQDWAKmlk*9@^f!rUbN3Y9ch7}oH^s*fq6GyAxz?N|0KmXw5CEAlWYGqmDD{Sy z4WDBw@Vn8p{4P`ektz(!^6yjKl=_t|tf;^mBz3ivA>E5Ro@avL!{wO7)Kn`OGMEjn zNuFqq`LIeT0YpbOACS-=DpH81@0)pS$dLRs>1LmtNgF|%Qm?7(tMzPpEFZ))r_XVF zdZXDAf)}Ic-IpQnu26YAWZAY{o5m4j;%fVftMgnX!*UZ#kNXu{Hz%SMfjBf|)vdeG zdCmx8m2=8a|3CFdo)>>4uBOE%O;B5W5! zt=P6N!L_o;H7&=}LGbtYkU5SJtsHqPO5GF76G%bTjc`-ZGT?rSuVCGpar+yqCr+e| zH$kw5(HAPGP_3NeYO7{|d z4xk|k^Qvs3v)8T+#hrgX?@>b_uEc&U=+>tj0+=C3zSBJFQoJjk#T3Zk|A;Uw;(d`u zm_S3kBG>~(_&R!WbP`x}dQ4oaZf2`ui^Cy4qlo3KzN`hV?lWp3j)Nc_|PP8=cSzw^KOr3mt|nAjob1g$Zp%T`pM z0J!L-{{-gTmX5}@rcS^hi_fWCqB?ge>!6{2|pteys z8tQ_h!nDlBf(i3y;tol;L}4)PoPBm6F1G#Rd+W-1X3dd~WIpB7!9@G=-zvTvZ^YVv zP1NjrqZm3;-$hB|lEmhndbJ!JKSnv-T+=_izR)eX!Qj_vpp6643ZZ!QDHBSG#yKFP zS@Y3FAQu9Ehho5y496n0!B?0G)=omV*^Afo+MY>;wkT1bcDwR^N{-~&#L*SPPcU;=GcnT=%YE1z~zyU0e>WL4)@7kS5EO8_Tmh~UnS^x1rkDF&E8B7V7 z#gmnan)8GzVxhjoAM0pI@wn}=DahplXI`@IrNzxhz^fY<#DmB!!r=I&rJ^7VKpAC3 zIrnXvo6ERjpPV5i9H=v#1I*FL)Cvt@B}p33%4*u5wTdjq_`(r-&my&C^w8gs_J}dv zAlPb_Gt<+`{4h`Bn#Q@{GanntKi zD^7%y!4MtcFDAf!;PXN9EaT~9xw^VshhAL~glr9=C2<+}9-N8M;|bBTkmlxzk^nJq zhm@h5)$sbL*E5E*E_|GxAUNwb;nv3+ez&7NIYm({@pkW&Emgr-UawQj=Jsv*DFQKj zDTehFmU1Asq14x&VNiau}vN7_yo3gd_v4(i-A(n(J61CW;aR4Fa{~OwM3VdS{SXi+jnNG zO8UIY714_RKI-=qq>SPLVGtxKNn=a_#$z|g=ZXpDO^210_s8l&Zuh|5&?RX_iyXKz ztHh>i111T_me`U|>!MuKI3Dz>w`~|L zA}hGH`b@}PoT{xfc3Ra|w;0*~vWEg3qo|X+`!45GXpt5!-WgE1RBur1t+fYzKn0){Kk-@u8-}uWNtn04!GD8f&MOFw`qelE`^eW$Y5=S6xmv50w zk5?5ukF=n~_JL@hd$&>c%x6o-3U;b1nr&#UJS#06J%+kOVL2s8Cyf>kM624`WTht| zwgbJ^?xUbL)RxLk^s4VPunZUeFz#U<-j$3gS-`F)S(*|hR!DByln2u z{b5o+Y4(bQP_uL*Oaka&P4p?NkdDwrFB2z>b?^51Q>pNWYp=HB;^jjf#)_9o%h7Q( z@??2%J?auj5Ig)V-WyAa&wP*@qV=DPW;u>_WQ~Lw6V%p{m-u5OQfzyYqK?^9>vpY6 zgRB21LU;>9L~iI1fv54i@k6Dr&l?+37OWg{!QW*+)rC32Xy5*b28W=V;7FI!_2F|R z75oa85#|OjIq=&q`BFyLGYW!`lhD;EAfB&Ex#(srh}>iR)*Sf%@js11UZokDe`lbs zzWrzT<@^JJTE0>dvo92mwalB$y!2(|5Hiw157e{Ah#n&D?z!l8IVrS%a0q-ld_L|u zCu?^>Ei5fqi6XEd$V45b&&&jZEgA*m7@6qFQHhii)49+h(G=mK6l3xQYBw-35j)V&q-aANY##gXbo=hZ)=TbiOH~RvM9-stjx|Ljef!ec?Z!=;*%5mj(;qX@?{!TC zJPA@ih1^!A_IOLbhe!i^$)&0*r3(iz`GTt1u0*4RuM;PZZwEfou#RVmuf$TWKD$|V zUE@a$4Hhe+{j7?stah5d$9B9(K94PT@NGNxyQy7$ObwU7l%114Sx2Sh`VH-aOF@;! zg6Yn3kt>B#zc@HwwNZI!MW9E@@V=(RVai}RCzfow({nps{`M)(rorlMv2>)-Q!_dX z+40Da8=kjkZO@QK%@G$H5w~p%&pS8L70)z=)7$)LO-&3bcjX z9Z86`rpOC_=|53(hh#GIdVVl{i$B3W49ZyY=6D?Dhkc=OMW89zA49adu|4m^p(29% z?)m*yd_DR0EvWMD(Ux^har51Oh({mir>BkB=ZysbK-hx-K-5rOgk*{2$x2E^zbUn% z$HJTTVAWKh9?o)vZO|^DyF$61KS?vYPS3`t$VbMLpB=+%mz?KpQER!3l*35p`d{7! z;=}PX;`;r__tjKnf`i47s7U5eMTq>H_v=w)9Tu1BtR{ehF;rZ7%>mk?mhka81%rGc z>5K8xD&1+DJZ5x)cV7Y>{&Mf7(DiWc=8!L#&+VVZvCuhn7gt$?!J8 zP%4d4N+&9E3L-0uQ5BgEhwN-2R`fopfwH%X{p)kxKH*GB@m}BVZ9roA2x_W9~|ZPn7VguYTXs0-Id+6#PE36 zxpwr_^PFpX<3*cA0Il6IX29JUbc_;=kW=x-FNf=prj=P4d?{jyqY(fcS66f0VIJSz zBfA61Pfip3|) zUh_;^K5x>kG0(vW6JRuFMCn5P>T)GXt%IMzyhopAm^2%ZR%1sFj9z`AJCa@vg-fGlko7*wuA@1nd+Jg2|CgGTkMq^CrIwuz1becddf?4ih4h0lB z^zdGMb)ceWFc6OZ85tkcpBhY$A33(YoS`iLFaA6Rh0Ku2YGo?I^pIDA|K%Stoe9pj z4*TG7MOiQLr!pw#?9)n)m0An?ux~yu5FT62T9)B%47G>7n$??BCTK!l>43xA@Q%}63k^?3%8Wu=RZSU4Cp@j&(@H-khnds9 zz7wqJEXVogUWyc&v(PKDzdQwtexgN2pRklh`}0$+_fXzWG&}L74h56T8vn=tCN8uJ z0*`ICuu=;-On=co=+cl#)!H_gJC0tVs}S0#eM?D{!J*IF_u7s9hW1es~Z+IqcM%h$m274^-#IBZ-6L_l8hHM~m$V!6xvkqDbxN#;%yNhW>_cD{}Anz|a`n)>9)65-%mC5q*qYA%X!r4xD z`NE?YZD-cJTqWY>ye#2!?V9*QH*2@fr$SpW-WoO72=FL~{y7Y}c-pd-hdC9`MvOiK zwx2JLY|p~DSmj#mwT7Q}61&mPoNYAtU5{R&k?3s^YfkgcO?NsX=4*_Ls-$6-i@4q5 zJzwn+Er57*7Vx?1xB{;b;J^?b@H*O+f&3z3?z{68S!?)Mg1+0HK5 zD!3>B#P(3#ss4}u6%Zwb3?BQ%qE{ZCg=u{xhe>2ka&B%L42>&N=xTtheJu%Ce6@Dx z^{xc*w+Nj#nn@?Ms|*v>{9=(_gU7c%zDe9gjoRjTCFF6 z4pF8IRYCzX?BD?g{O3d)zcf6J?+|^L{t~kbD?6t9D*>*s}LXoIGu4JFy4Hl5KvOaHwDBRzH5rApae;hvZ z4OhY@?w(J>oeKw+^hcWzv~wm(87^&z7OuBMGffsr_bu7)Q}&`wsTr5^Ekm!LpA{af zNqJeq(8%j=9bc+UQ0PoczBktZu~)Z8vn zy-8D=$zzR!5qZ09^7)ZG{Nt=fBbJzk?UXb559gdxuU}d`v^;{L}P-p zy~yCySaX>l1(&6!o&310rrS!H4RH+t3x|KYQx~fK^EzL=Q0*YiC6ov3kx69%$W9~@ zSU*kvvg!Yg{*5(i{Kh`px5+a`R$bVL4$EQ6g|f#lT+$R;rx1q$wA88&pR+;Z_Z6hZ zl!nqfa}3Lo9=RhMqFeP{zXu^fNnH_*&Lta@rRzT>R*W5~@t>X~dp? z_MN#d+f_O1j+N`oAf96Y0-EeKpYG~siV0MSqz8hdl+?ux%@{8ND20r~u{Vo4<-NZq z2J0L<%^fLJil1&KRSBYS>*Y`@clO=i++I&=A==q={B@qge#!1aFA7l8b+iFyLR;S= zUNPj6kBbR#O~Afy+Cm0W(9uNu=(rS#RP+ubk!6bY&&`?29TdrgfK#w#r_q1>uYnOxoU21}cdW*~h(76)q44 z(r(wl^`HmPhvei9XYf4SpcQ%7Jt7JSTjX}+MXlf20abEK@Cn$|u4ks_!w^N)+Nws@ zGvba$CJXaR*>=$5w{HSE@rCE$=;!}c%?U{)w^G};3Uf|%DCJ~7f{{w`j>CSIE{K0b&@r({%$Ah- zSSxTY{gH7?=4Uflai7WFVg7!JL-$tKz*Y}MAB`eQ5}CHLfEXSU%4>@xMQhcor5b4ow8zAYKctaVIy*dLULoi4mgWm zYMhtvIn=UO>?TCCiA?oA;+P<%ROC+7;5a+Ya3!7ZZPVQt9ov)FRnx@T^6{+1Yx&t3 zauV`ZfIMKDqn?$eH0Bj3H6|N3s43UplHHg|Dys_4;!e=_N6y)`2O^2Uu@b$5zZZ0n zSLx;#;O|tnQnMmzlsnfg5@1gi!zH#{|KTos$LFbH>HEAjDdV%1mnkpr0(}>cA!quX zyh1{|POdLEJX9Bh%DTfQD)ThMgy5|IsIpOSN$8*c&wwFQq*7QPrC@4B@)4eDyqLX) zcvD-^l5b)x$fjU37hXhN4R>m_7~dcqDnBs)t5&aCR71jdn^U$HPEJq}*QuC1U5z?_ zT+d(=t%ZF}r2lUc9jN`T)v720l}*J9YW3DUqOpCld-#Kvj9F%lNyHDHzl{eBgxc@i zD3;z%-8uj9=S&kT4KpZN4!m8};niFPUugQLFHm<#J@j1USuRYyK||SIlqd);voR0a zvQH!-3ju)2>?q9Okl`ThL{#3-#NeIt!sgQ8U*Ph-qjNGbi{!VTYaZ^8A9_Wk2MRsS z=N?*pe&~;r{&;dx1Td5O|E)~r*t;&isKg9nvCqilb#jwB3dfcc-F@*viwu{?YW_+?jOBDo(_@dZLs{Y46F*V14f)uYrhKDps2D^jf#a3cSVZ-zdfBw_| zRdoLeNq_7+3z(k8in*t}DCV^(xb}s=-HZNzz&0Cmb&w-;NzVOz2@GEU7*BrXcnT4gl`%}!A9x$gCVVOMXE9nE3cJkX zIe~)tdS!zWaQ*LwX7#+m@?9I<l%*%RzG$cIW@HO=W#7pXJeYNF}Ji9T7&*n6|9G+HsDNf%oKUksak3{&I zsXBxtMTg3P@3KTO0lUgvWUFp*^7XAQ#VRcMbz#w*V+FQo4EQ6%mfkC|E?uW!-J6@4 z(KL&qBEOK32E64bbm|`$sGPjk_;|xqRs|r8PX78a)ItE+N+BtwS$eYc4+V)A@56~J zoS477y1&sv+Z0*Dkxl-${^LS!UiiP{Kb5xaB0OK}UslGLs`dv+G%@Bw-LMLK25ap^ z_4(CYzSQUV*%r}!oJsBKl&8P!NTcEM^4(F72xIY6Pnybv$GvLA(}vlSq^=Z2i~s{) zjx-vIiYYjm8JUhc$XGbgV|Fxq5?02;ga;0|Iu+m zY9FKM`gavqI@g`!+K45BM)#nK!|A14bjkM4H+5SBs+d1xDI@^FeA-QU|BOaB%Y-Fbb@fz{nDkqVPL3J#TiP&`^w-ITXNT;p<g0|$#_E+Zg4y)s7qIklF{ zPepR|5F}rIr~|o7bzEo5m#SPu6Bf4HHkRi_dCENYWR8E+qB2U)E0q4Unp8odQ&@;$ zBf}jHfr)^#|{QPA3)~|BquOVX0{U85Z z5DFE^0HH5jT^0_lbOrN+?4DoW)s2rtil`XdWlEM~4=wFuj{CGl^ z!oxK2WdPFbJ3$9@TopjnP-MfH5gK4z;zHGn8@)M#Kt4O1&7K9m!it6wrtooRgBe{uAc7f0ORd zc(^+KC}8k2Rb->2=XmmwkD$dQN=~=77A07X^P>GZ`U+F58R z<&#?55%+iwEIDwj^6{Ar7a7O;ojX=W;d=%>-dxXRtP8XkZHLtQ1vOz<9Mfgj5BDmh z4@TCM?}>#wYvPZGQ*XeSyz64mOSAD8MGJ%6(=Ew{ z#Bd7I&DqqP6b@l&)R+8PA-K(TIiiTc`0RQv(2=>CDZL6u-(4G-_xqM(qFe;okBviZ zYf&P_TQi$yF3xwxY>h`vC}3ell?>S)CysSr2;^Sz>fK90(jGwFmmHfTdt=^POWR1C z_*ZV$j~I<{5z<1bz9Rx2$L6%FJnhlQjZ|3g9AT&EjSe4P@Y15 zr3BJj!sLCZBdf6%mRT)SamlY#L=x>-S+r_L$r&7;mvk*tY3n{Q+#qFJ}}T?7g@g|<5+9juf2Hk5pR0o zFa45#yCRhg)#H{h>i3oOR1yS2WJuXyqg&2Vg~WBDnQwh+V;=F17sRDo@ZvW7pGlX$ zD#ez5@*{m;`SrPu8fuSVLk71_qa!gZzJc(bzm^2bC1i$2S76B1!{g^|NsDUuMs#dM zX(S@)6(3d_6U$!*p6zzj>Tpk{8-gOyBh~Naz!p_T)@sb$2QhcjA3uulDNvKT!Dws2 z1JS&5(&^t@o5&O8d!YlPGhhBp`=Y!?jmsl&mG^7LvvMO${?t*k!;QIb`|V5^q`7Wui*)>9Dvr>68aYW~F!z9}3^$BzKIbiO5$jtv~Q6%V0987nwj? z#WqPu6`u58{{Ywb&zdL}N3t7eVwN?x^9qIIE1}UF>0_%3q1IErCy}VE@92nNsj6y= z_g>G7JYv9u?}#j$ZNXo!*_7qVIvuki)$c{h;I))iKi^t!D9^g9R8-j|iA}8!nVhj% z%}1U4YxqZZ1t?3Z$mr;x!utOh(GF_NRP1X>$G{T`$ATq@s2I@DR~1XAv6>5K&^Q`{ z=Eht`8?MH1!ZH@UxJ%ed55Ch)AJ88!EF9!$zUEU=8e`4&!PZI*wjFD@$&e2==mr6R z3&F_+>zkQP)RB@H+YRmfgLFngB?I&7{V}=Nl5x}K`o4MwQz`q1jCGI83TS-u*&wqff7pCrFl9%(}dqXsdpYzW5mm-|qu{~BDp6}a$~35^VfWHMWIC4978 zN)%12ba&ZQbYfZ{Zp&$;?bQE21jBL9koX}HOVMXR zJreuEhF~wapMhB8N=L028tE_3t!#wRG<`;o&?q%v4W zi;G-J;wy!nUTae2WER7W>hlY50X=?;VM>>J?x!9%gR# zJJp}vv8K5T_3IiIHH|dQszi016nO*;+bFCe&QpahRF;I~_ZSKprT4Hx0u3re&ysvL zk}{Ok@Lebr9zn-K&&#J#(Uhj!mfaZSByXl)3EVQbF7DqhU^TaOX+;GcmCkp_DoDA< zcc`Q;H$6O9^9I~YBi?#AIi_A;LmtVIUt5x*Am_d`(L(kldV?dFNQXkIwMbNK`-aL? zhe=F)sa^Di)K*w8+@JsEP&Y98V>vy@I4Y_!bRp<3e%{0lRWO{L*J110yBnrlcz?N% zH+{lLU;jH@eg_)w{?zy3Sjw}wRY#%%z?G~gO~`!)90;}qG$>Oe(>nl+H3HZC$Wi2D zhKb#P{j+SQXQy^kj~nDS70=4Q{^=hP7aGpfi#_O2Zg5CN8=K+-@f73u88ABz@Nokd+}u$p ze(9-9&jiWngh?-H?uU>Pe_2;}3};qyG7vI?cu7C&FQ@SiQ!8<|vG1fAjKH0FZ?NbI zm6GkUQ~4U+W?NoGlIj(|`n3R*Q`1H>x1nSl4BEEu^hz%5LpOvB%%k_d_>jZst_6ck z_ZWW{uk|B_bM2+McsZg|8_0AoH6x-S#eLaL(Rzu7!qI#}>0-w%Y!X#(Gt*w>NuV7& zB)r}7Vqmv%E$3g`_Ol7bZw zIjgusMELdd-Ouu;1FS8&4cZit+bu|&7_yoY8h58eNT&VM|3lmrHMc~pIdP*I z{1DF*B#WgkgU)t1B>58mgX9V`}2g~$j2fW3ss7fCMO_OXE z9<`<|p8o#rJb3bI9LJiM6`|oa!3M|WmM<~AKgFUm`D$P%NjCpOu2Hwg_}947<17A) z9v}3oN^fT^JY?(llli|{L$9!cn<0ud(&(|G5_kzByX?kapWeD1xO4`dkY8)eEsc3S z=lpb5x}hHe+8z~x6tS zI#Ys_K&21j{3dOfk5cq>2X~yq6Q^Rfnyo!K&FQb!zv|xEJS}JiidA3Ly)FFSEv^{+ zY>-0iTvv}bA@~SA5k)7@`fCRy8s!aSJhfB8G%YV9HPM&vr{4++pWO=s(eBmKme({Z za(F7e76j zn*rQeI0iVQJHkab*EfTV*J??4!a;TOy0jAND!i;Z6-4jow%w%{Vu4gaYVBaJkTctG zb3#TuWv?PjC3+qmHn1eCP`nr0v5idar=T=v|0?rH=yA59Rldh@d^UmZ>lprP!O9a) zLji~*Ig9)_Au)_W3pz0UQHWALG*ylpAgukRFxNwal=9W@Aaps%lUWKz@faKyg&W?~ zHWZvIaq<=a%WedHd5Df!t_6NuLh}Fk&pd($1YY%G`I2qx(L?E683Zxcg;3d@h9o`F zH)Xo&XD(BlSB`Z<_JTOPy=1Y(hQf2%b| zSN;_uR1Om%t2||!_|1Xq@NvMGXr@vM7TMwY`3rhk*=pdgn*>s0xUbVV(Gw04?pUrj z2=6Cjpcu^TTLe9uayN$NFqvkaPf;IQmRuKZrf^Uc)JA~@4U_a*p+YMfM;0`K4uM8+ zXwkFsb7j>Cg87jgZ)q!K8u za0$H-D83_55M(9*RC(#)l8vMtohmV_&?!^s6IiROj-jw8aGk5H3f1_S>_3;mM?;U5Tf0i*to$aeWE*G z?Vo7w;0b793z@kY=bOxtFrzo{b1+x&y^4L$@uxc`U;gn+4Z7>o%8c*Nbn63!#LL3u z3tk@L$l*`}8IXGLOi%K`&ra)Wi)@0A(T>eG6^5T9Cz1qpP`$dB@P|zbql%0Wp66N% zvKXa%qd}wlMWv~4nlzgwku>pm%wEb=YSJ6t@5po=k^NN8Krtc(lVJxfHE%)KwyD}2 z?KB(Q?PLr5PYYw;n>d3FIm#tNqmmRFd0n zhvkOLx06Z*uV6$&^0Cd!0SO-n)Qv}zl|n=045))%`rI7i4|Zm&Yr(%MDeray#lv?M ztr>t++5`8e|M=g=h0@cB$EuNQs`Y)*bOiBY@(Q3!nui2KBDVD6Y0c(;78JNPTIFee z*^n(FzEeB7N0euIqaM*1S6=K6tZvvGER1Y>_K;Gc3ir1x2sc80b;N2<@He;990l7= zF`2y)z)OAn{M7$@-F4C~af|aotC8MnzAT=SK>|U*U^Zp#I22t<1?0<$ z?qMuBAYl>0FlVI}K8oBlyDeCpS}m(|qMTbz6dKp>n$h+1FIw{bLgJvEl{!Ns8LW}} z;4VjFV$IWOW@&vKosU~~=YydA0R6X5snfQEC@we{EFTGiO33%9=!q`>+lrafSn@$@ zyLD17lMJQqDnlXRi0!3H984C8{_4}%L+%c#X)9$%D76r z1nnmeOBrTO%`+$31|>GXfWVSkDKKNAMALqd!{PXl;&3LTG3;vvGO~sNy9pjiIb)fO zt-+BNxiwLEbCqh1`ozlBI2zHfqOH&ad#TBhb`9D<*T%(A)^_-<)H{~#$<|d`ARLlb?NTS_7gIFUm|^bc*w*P&+wITrR;$wSZB?w0 zGx5L67SF%!sX+h$bB?N*r~8%zq1WJESNNNygW`JTV*`Yrtw7}65u#^F+q=4qPv{ta zP@EUZJL2`OgJFC$rj5iXK3L;rO0w6&E~g(>D~icgkcMv=dNK`Z35oyqL~tS?RMF4C z1J{&_-euU@68>1Sf|RCALTSC__iEkC58KqFEWP6vNp53=dZm>a1n1@H5O? zOGv3{2qd+|tHyA0d!BD|f|*7c^WA-1kgKXx(;6w@MF;0R78M&&nQ)YDQB@SK_31^` z1?`3|^#+B>?+t$RY-?!!a=X?~>kz&>iAFt6k>FV;!XZqbMae?DsUxQ#CoE}3 zh#jO1eF&^OvyJDH-=VdC&}j-Fk+;yNmcqbP3%znRg` zUPY6Pbq~0;Tt4|YZqvZ=>${V~Z-)};g{W_NO!R!=_@oCs=HChF4fj#b)kO9w6B#Ph zsAaDv`qcqB6QXI_Ta@NDAqzYmDxZ&?uUNv{-M{|dp-}qU3 zQ|z@fSF>Y(pQ;pqb0wG1ma)&j_nbc_0H-R6w`#ein#BYp0t@5Tu=K1`QqoJex3sxn z=^-+8cmk*FNT{N2rz|(JKolYp3IiKEwo}5|J&K1jmp;JT4yNfiiP-;q_>tMdOhh`Y z4NBK^t~s(a63KaA9ah!~v8&0bzH&*qq7m$Lyp3T(p1R0T`Bh680S+tag-@C4jSK{r zFiuT2BuFn(+I6iwnSv{Q|0}{|&iiWuF^iZA*DC1RmtS74%|Bb8nF9B9iqg(P4TtFL zwguQbUs>1JMf7wg4TTIMXN|oJ6JhHR)$S+GpF`{#!YEajnPBi7$eWhxN4W> zDX8o{(k%?-oC{nBq;*&LO??*(u2GsRX>Sv&tgimy_fP+bI4KdWo)mfVZ6(Q(8~acT zTt1UODiHUm0}R?xN=jPl5v4U9yCb3o)o)*7h+Wy1KTA0k=sypJ2=_PF_tDe7@mz?< zrVHU?N#>IqPNr7Aq#`YGcdpf64FCPxd63!h?*$uE3{p*O`dMiX)Ro~ME>2aec-`{5B2q6ZK8pdRoYxG z)y**vG2lv$bD`n+%6 zDu0z?3E7`&*`K#_o2p2}@)-Vpm%Dgw>xg!57}26EiLU*GrV%BbWn@DsAM874^<>W` zD*aj&7AAQ+IGIsE0l8+mfd3q_13UbeAh_?fvbQm#lz+1$akG}Lfatxk%xw+`h0 zd;iNr+(5E%H>>;ysXL$)&9A}7pCdiJ6`D$QAFji-{HltJX~+?vwUUpPB+WX8SiyI*tNPoGEY0={Fh*8pZ6tLt6G`a(1$ zZJqZhajA*q&IFip;`{X^Q0qy<{`Y_Cf9XKfSjYx@nP%NYBzn4%<*BXi|0}PARw_V~ z2MHA4O_(?BDt+-dBuUQ|L7&RBssZlB%Ov4@&tYQhhZfpf2{thF=~8bejph+jXv!#4 zX>%lUJ3p%>R&P(gb+0s?U}($`CLuc_aZH!dJ@69okExXWCjGE=CM z(-90E!2ItE$C7&Xn}+5D&_Ln;^nVI~58#Le3CpYN!(SCRV)3GTQDI4RhnS0Fym;tl zLSu;Nlc_bYA}5H6i!bB;%o@_aQ{Rw%t;EB_YP3plB;R^O6``HMW2~621c^&t3CJXJ zA_}$O^Vm+IDgXg)4AspUhDC5yKT&axi;}#uAzfoli6l5WZ=ra;`hZ!Otkd>HeZKLn zrH54Iuc80r*>E1h#nZ&%RAuV6O#68MjZL_--+lQr?hT8b4)%X*iQpBzvW-mOXs`U6 z5a-$qe?G23w?W5017OO55jsz1a6wv^YA6o$5I7H6dJcuM6T3 z$;J1YE0lgz&~BR}U|SmsfEXdU;fI|t8nT->l^LXpxm|OBn-UG#S6v zlWm>u!+0nB6QT6?rX^~EdCbkn*QOG84sMC4Q~I&&Ao_{*&17ij@AE;nSXGvLMBexM zI@Yi@h%XY8I<8g(To2#vo!-q>LtS#Pn)@P^46C~!^2@ICN<TTq}C!_U#`lxFP_ZccG5=)^pRK z&$MS#e0kbNh8t}hHZCs9>0kNxhf%LyIxCA#J5>h#nmOmLLH1JT2Q?>33^16&kaS z5TP~}&9K>;@*PnX+Rq|Kue2#^ddrybBzwlSxkpvKU+Hy8iX_84P5Ej5`Iqy*?!RE> zge7Cmu&0PwW!W(Ww@@@_Y3on7DK_&Yi5AgkEPO-+_#AJ@#Q9wufB5*=vr%f6siENF z;2ci)ul^-{0S?86cVdAl2qc1E?c?GSVr{A)vcsIlBFU(by5hEUB-REU!csI{P?!@K zN!}XW6$1_TOt0ty>*txL`H2p|e`?Az-$qV8?vjn^-sAJjH`SOG|70IU@NDIY-s|d{ zOX;XRj_1E0UEmp4ujkWGes6frI5hT1^ZX?dx;(Tk^~ECzF!G_!zm7iEd21jXVk7d_2WK0B@wbv6Dvd; z!|!osj;>s;OO4VzEN^Zm6KGPnlyg3l+A0ea5Bz@3rXo*;y0iaiY0$+FvIk)A-ESM?NZTjbmD}kD~7l4AFDy21!iXGDuP+ z)t-wKU9U;|OqW08UJ!F|AWGIh5SbDsTXx8=C7OClC4B2ml*MB`kk<9mQ&Dl@mX7AP ziZ<6ua4V{PL!{+5_A36DdIbC(TPo$7BmcC{~Pq6%mZc<2NLK5lfY(r~O41+4MR% zFG9I6Bvf*-rcv`*pE7#@!I!Sj`4=&|7!rbdXyvdpvFpf`u5ab>tp76MC&JbO{)j*{ zj_x6>5W1K#k#Gv%p41lNSdW)AR-=lcF)EYc5`_g7Y%o>x(@^>^KoU}LP|l2~RxIXZ zSUBRZ{wKt9;fEX6|9c;(`hWTP$Nw&3?uNK2R+$`byfy4@slce37MEJf=n7m%TacJ! Og#N$$e*eEe`+ot3YnfR9 literal 0 HcmV?d00001 diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/FakeNotificationChannels.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/FakeNotificationChannels.kt index 194caf7287..94d7905f6a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/FakeNotificationChannels.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/FakeNotificationChannels.kt @@ -9,11 +9,19 @@ package io.element.android.libraries.push.impl.notifications.channels import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.tests.testutils.lambda.lambdaError class FakeNotificationChannels( var channelForIncomingCall: (ring: Boolean) -> String = { _ -> "" }, var channelIdForMessage: (sessionId: SessionId, noisy: Boolean) -> String = { _, _ -> "" }, - var channelIdForTest: () -> String = { "" } + var channelIdForTest: () -> String = { "" }, + // Side-effecting recreates default to lambdaError (matching FakeNotificationSoundUpdater) so a + // test that doesn't expect a channel rebuild loudly fails instead of silently swallowing the call. + var recreateNoisyChannelLambda: (sound: NotificationSound, version: Int) -> Unit = { _, _ -> lambdaError() }, + var recreateRingingCallChannelLambda: (sound: NotificationSound, version: Int) -> Unit = { _, _ -> lambdaError() }, + var readNoisyChannelSoundLambda: () -> NotificationSound? = { null }, + var readRingingCallChannelSoundLambda: () -> NotificationSound? = { null }, ) : NotificationChannels { override fun getChannelForIncomingCall(ring: Boolean): String { return channelForIncomingCall(ring) @@ -26,4 +34,16 @@ class FakeNotificationChannels( override fun getChannelIdForTest(): String { return channelIdForTest() } + + override fun recreateNoisyChannel(sound: NotificationSound, version: Int) { + recreateNoisyChannelLambda(sound, version) + } + + override fun recreateRingingCallChannel(sound: NotificationSound, version: Int) { + recreateRingingCallChannelLambda(sound, version) + } + + override suspend fun readNoisyChannelSound(): NotificationSound? = readNoisyChannelSoundLambda() + + override suspend fun readRingingCallChannelSound(): NotificationSound? = readRingingCallChannelSoundLambda() } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt index b004c35537..c53e98bc93 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannelsTest.kt @@ -9,16 +9,23 @@ package io.element.android.libraries.push.impl.notifications.channels import android.os.Build +import android.provider.Settings import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.net.toUri import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.services.toolbox.test.strings.FakeStringProvider import io.mockk.every import io.mockk.mockk +import io.mockk.slot import io.mockk.verify +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -45,7 +52,7 @@ class NotificationChannelsTest { val notificationChannels = createNotificationChannels() val ringingChannel = notificationChannels.getChannelForIncomingCall(ring = true) - assertThat(ringingChannel).isEqualTo(RINGING_CALL_NOTIFICATION_CHANNEL_ID) + assertThat(ringingChannel).isEqualTo(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE) val normalChannel = notificationChannels.getChannelForIncomingCall(ring = false) assertThat(normalChannel).isEqualTo(CALL_NOTIFICATION_CHANNEL_ID) @@ -59,7 +66,7 @@ class NotificationChannelsTest { ), ) assertThat(notificationChannels.getChannelIdForMessage(sessionId = A_SESSION_ID, noisy = true)) - .isEqualTo(NOISY_NOTIFICATION_CHANNEL_ID) + .isEqualTo(NOISY_NOTIFICATION_CHANNEL_ID_BASE) assertThat(notificationChannels.getChannelIdForMessage(sessionId = A_SESSION_ID, noisy = false)) .isEqualTo(SILENT_NOTIFICATION_CHANNEL_ID) } @@ -81,16 +88,418 @@ class NotificationChannelsTest { fun `getChannelIdForTest - returns the right channel`() { val notificationChannels = createNotificationChannels() - assertThat(notificationChannels.getChannelIdForTest()).isEqualTo(NOISY_NOTIFICATION_CHANNEL_ID) + assertThat(notificationChannels.getChannelIdForTest()).isEqualTo(NOISY_NOTIFICATION_CHANNEL_ID_BASE) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - reads persisted message version and seeds versioned channel id`() { + val notificationChannels = createNotificationChannels( + appPreferencesStore = InMemoryAppPreferencesStore(messageSoundChannelVersion = 3), + enterpriseService = FakeEnterpriseService(getNoisyNotificationChannelIdResult = { null }), + ) + assertThat(notificationChannels.getChannelIdForTest()).isEqualTo("${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v3") + assertThat(notificationChannels.getChannelIdForMessage(sessionId = A_SESSION_ID, noisy = true)) + .isEqualTo("${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v3") + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - reads persisted call ringtone version and seeds versioned channel id`() { + val notificationChannels = createNotificationChannels( + appPreferencesStore = InMemoryAppPreferencesStore(callRingtoneChannelVersion = 2), + ) + assertThat(notificationChannels.getChannelForIncomingCall(ring = true)) + .isEqualTo("${RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE}_v2") + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - creates new versioned channel and updates current id`() { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.Custom("content://media/internal/audio/media/42"), version = 7) + + val expectedId = "${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v7" + assertThat(channels.getChannelIdForTest()).isEqualTo(expectedId) + verify { notificationManager.createNotificationChannel(match { it.id == expectedId }) } + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - Silent produces channel with no sound`() { + val captured = slot() + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { createNotificationChannel(capture(captured)) } returns Unit + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.Silent, version = 1) + + // The last captured channel (the recreated one) should have a null sound and the new id. + assertThat(captured.captured.id).isEqualTo("${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v1") + assertThat(captured.captured.sound).isNull() + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - SystemDefault uses Android system notification URI`() { + val captured = slot() + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { createNotificationChannel(capture(captured)) } returns Unit + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.SystemDefault, version = 2) + + assertThat(captured.captured.sound).isEqualTo(Settings.System.DEFAULT_NOTIFICATION_URI) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - ElementDefault uses bundled message URI`() { + val captured = slot() + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { createNotificationChannel(capture(captured)) } returns Unit + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.ElementDefault, version = 1) + + assertThat(captured.captured.sound?.scheme).isEqualTo("android.resource") + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - ElementFade uses bundled fade URI`() { + val captured = slot() + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { createNotificationChannel(capture(captured)) } returns Unit + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.ElementFade, version = 1) + + val context = RuntimeEnvironment.getApplication() + val expected = "android.resource://${context.packageName}/${io.element.android.libraries.push.impl.R.raw.element_fade}".toUri() + assertThat(captured.captured.sound).isEqualTo(expected) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateNoisyChannel - deletes prior versioned channels`() { + val priorChannel = mockk(relaxed = true) { + every { id } returns "${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v3" + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns listOf(priorChannel) + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.SystemDefault, version = 4) + + verify { notificationManager.deleteNotificationChannel("${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v3") } + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateRingingCallChannel - creates new versioned channel and updates current id`() { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateRingingCallChannel(sound = NotificationSound.Custom("content://settings/system/ringtone"), version = 5) + + val expectedId = "${RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE}_v5" + assertThat(channels.getChannelForIncomingCall(ring = true)).isEqualTo(expectedId) + verify { notificationManager.createNotificationChannel(match { it.id == expectedId }) } + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreateRingingCallChannel - Silent produces channel with no sound`() { + val captured = slot() + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { createNotificationChannel(capture(captured)) } returns Unit + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateRingingCallChannel(sound = NotificationSound.Silent, version = 1) + + assertThat(captured.captured.id).isEqualTo("${RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE}_v1") + assertThat(captured.captured.sound).isNull() + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `recreate noisy then ringing produces both versioned channels with current ids`() { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + channels.recreateNoisyChannel(sound = NotificationSound.Custom("content://a"), version = 5) + channels.recreateRingingCallChannel(sound = NotificationSound.Custom("content://b"), version = 7) + + // Each recreate updates the corresponding currentId independently — the lock guarding + // recreate* against itself does not interfere across the two channel families. + assertThat(channels.getChannelIdForTest()).isEqualTo("${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v5") + assertThat(channels.getChannelForIncomingCall(ring = true)).isEqualTo("${RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE}_v7") + verify { notificationManager.createNotificationChannel(match { it.id == "${NOISY_NOTIFICATION_CHANNEL_ID_BASE}_v5" }) } + verify { + notificationManager.createNotificationChannel( + match { it.id == "${RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE}_v7" } + ) + } + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - returns Custom for non-default URI`() = runTest { + val customUri = "content://media/internal/audio/media/42".toUri() + val channel = mockk(relaxed = true) { + every { sound } returns customUri + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isEqualTo(NotificationSound.Custom(customUri.toString())) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - classifies the bundled message URI as ElementDefault`() = runTest { + val context = RuntimeEnvironment.getApplication() + val ourBundled = "android.resource://${context.packageName}/${io.element.android.libraries.push.impl.R.raw.message}".toUri() + val channel = mockk(relaxed = true) { + every { sound } returns ourBundled + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isEqualTo(NotificationSound.ElementDefault) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - classifies the bundled fade URI as ElementFade`() = runTest { + val context = RuntimeEnvironment.getApplication() + val ourFade = "android.resource://${context.packageName}/${io.element.android.libraries.push.impl.R.raw.element_fade}".toUri() + val channel = mockk(relaxed = true) { + every { sound } returns ourFade + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isEqualTo(NotificationSound.ElementFade) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - classifies DEFAULT_NOTIFICATION_URI as SystemDefault`() = runTest { + val channel = mockk(relaxed = true) { + every { sound } returns Settings.System.DEFAULT_NOTIFICATION_URI + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isEqualTo(NotificationSound.SystemDefault) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - migrates legacy SystemDefault message sound to ElementDefault when version is 0`() = runTest { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.SystemDefault, + messageSoundChannelVersion = 0, + ) + + createNotificationChannels( + notificationManager = notificationManager, + appPreferencesStore = appPreferencesStore, + ) + + val config = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(config.messageSound).isEqualTo(NotificationSound.ElementDefault) + assertThat(config.messageSoundVersion).isEqualTo(1) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - migration is idempotent across multiple instantiations`() = runTest { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.SystemDefault, + messageSoundChannelVersion = 0, + ) + + // First boot: migrate. + createNotificationChannels(notificationManager = notificationManager, appPreferencesStore = appPreferencesStore) + val afterFirst = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(afterFirst.messageSound).isEqualTo(NotificationSound.ElementDefault) + assertThat(afterFirst.messageSoundVersion).isEqualTo(1) + + // Second boot: gate (version == 0) no longer matches, so the version must not bump again. + createNotificationChannels(notificationManager = notificationManager, appPreferencesStore = appPreferencesStore) + val afterSecond = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(afterSecond.messageSound).isEqualTo(NotificationSound.ElementDefault) + assertThat(afterSecond.messageSoundVersion).isEqualTo(1) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - does not re-migrate when message version is non-zero`() = runTest { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.SystemDefault, + messageSoundChannelVersion = 5, + ) + + createNotificationChannels( + notificationManager = notificationManager, + appPreferencesStore = appPreferencesStore, + ) + + val config = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(config.messageSound).isEqualTo(NotificationSound.SystemDefault) + assertThat(config.messageSoundVersion).isEqualTo(5) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `init - does not migrate when persisted message sound is not SystemDefault`() = runTest { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + } + val appPreferencesStore = InMemoryAppPreferencesStore( + messageSound = NotificationSound.Silent, + messageSoundChannelVersion = 0, + ) + + createNotificationChannels( + notificationManager = notificationManager, + appPreferencesStore = appPreferencesStore, + ) + + val config = appPreferencesStore.getNotificationSoundChannelConfig() + assertThat(config.messageSound).isEqualTo(NotificationSound.Silent) + assertThat(config.messageSoundVersion).isEqualTo(0) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - returns Silent when channel sound is null`() = runTest { + val channel = mockk(relaxed = true) { + every { sound } returns null + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isEqualTo(NotificationSound.Silent) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readNoisyChannelSound - returns null when channel does not exist`() = runTest { + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(NOISY_NOTIFICATION_CHANNEL_ID_BASE) } returns null + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readNoisyChannelSound() + + assertThat(result).isNull() + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readRingingCallChannelSound - returns SystemDefault when channel sound matches DEFAULT_RINGTONE_URI`() = runTest { + val channel = mockk(relaxed = true) { + every { sound } returns Settings.System.DEFAULT_RINGTONE_URI + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readRingingCallChannelSound() + + assertThat(result).isEqualTo(NotificationSound.SystemDefault) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.TIRAMISU]) + fun `readRingingCallChannelSound - returns Custom for non-default URI`() = runTest { + val customUri = "content://media/external/audio/media/9001".toUri() + val channel = mockk(relaxed = true) { + every { sound } returns customUri + } + val notificationManager = mockk(relaxed = true) { + every { notificationChannels } returns emptyList() + every { getNotificationChannel(RINGING_CALL_NOTIFICATION_CHANNEL_ID_BASE) } returns channel + } + val channels = createNotificationChannels(notificationManager = notificationManager) + + val result = channels.readRingingCallChannelSound() + + assertThat(result).isEqualTo(NotificationSound.Custom(customUri.toString())) } private fun createNotificationChannels( notificationManager: NotificationManagerCompat = mockk(relaxed = true), enterpriseService: EnterpriseService = FakeEnterpriseService(), + // Default to ElementDefault so the legacy `(SystemDefault, version=0)` migration gate + // doesn't fire and silently rewrite the prefs store. Tests that exercise the migration + // construct their own store explicitly. + appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(messageSound = NotificationSound.ElementDefault), ) = DefaultNotificationChannels( notificationManager = notificationManager, stringProvider = FakeStringProvider(), context = RuntimeEnvironment.getApplication(), enterpriseService = enterpriseService, + appPreferencesStore = appPreferencesStore, ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index c58a29d866..53bea9e766 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -373,10 +373,12 @@ fun createNotificationChannels( enterpriseService: EnterpriseService = FakeEnterpriseService(), ): NotificationChannels { val context = RuntimeEnvironment.getApplication() + val notificationManagerCompat = NotificationManagerCompat.from(context) return DefaultNotificationChannels( - notificationManager = NotificationManagerCompat.from(context), + notificationManager = notificationManagerCompat, stringProvider = FakeStringProvider(""), context = context, enterpriseService = enterpriseService, + appPreferencesStore = io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore(), ) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopierTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopierTest.kt new file mode 100644 index 0000000000..b8d652bafe --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/sound/DefaultNotificationSoundCopierTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.impl.notifications.sound + +import android.content.Context +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class DefaultNotificationSoundCopierTest { + private val context: Context get() = RuntimeEnvironment.getApplication() + + @Test + fun `sanitizeDisplayName - trims surrounding whitespace`() { + assertThat(sanitizeDisplayName(" Hello World ")).isEqualTo("Hello World") + } + + @Test + fun `sanitizeDisplayName - DEL and control characters are dropped`() { + // Build the raw string programmatically — embedding control chars as source-file bytes + // doesn't survive editor / tool round-tripping reliably. + val bel = 0x07.toChar() + val tab = 0x09.toChar() + val newline = 0x0A.toChar() + val del = 0x7F.toChar() + val raw = "${bel}Hello$tab${newline}World$del" + assertThat(sanitizeDisplayName(raw)).isEqualTo("HelloWorld") + } + + @Test + fun `sanitizeDisplayName - truncates oversized input to 256 chars`() { + val long = "x".repeat(500) + val sanitized = sanitizeDisplayName(long) + assertThat(sanitized).isNotNull() + assertThat(sanitized!!.length).isEqualTo(256) + } + + @Test + fun `sanitizeDisplayName - returns null when nothing survives sanitization`() { + assertThat(sanitizeDisplayName("")).isNull() + assertThat(sanitizeDisplayName(" ")).isNull() + val controls = (0..0x1F).joinToString(separator = "") { it.toChar().toString() } + 0x7F.toChar() + assertThat(sanitizeDisplayName(controls)).isNull() + } + + @Test + fun `sanitizeDisplayName - preserves unicode beyond ASCII`() { + val input = "Element Tone " + 0x266A.toChar() + assertThat(sanitizeDisplayName(input)).isEqualTo(input) + } + + @Test + fun `copyToAppFiles - rejects file scheme`() = runTest { + val copier = DefaultNotificationSoundCopier(context) + val result = copier.copyToAppFiles("file:///tmp/evil.mp3", NotificationSoundCopier.SoundSlot.Message) + assertThat(result).isEqualTo(NotificationSoundCopier.CopyResult.UnplayableSource) + } + + @Test + fun `copyToAppFiles - rejects http scheme`() = runTest { + val copier = DefaultNotificationSoundCopier(context) + val result = copier.copyToAppFiles("https://attacker.example.com/sound.mp3", NotificationSoundCopier.SoundSlot.Call) + assertThat(result).isEqualTo(NotificationSoundCopier.CopyResult.UnplayableSource) + } + + @Test + fun `copyToAppFiles - rejects URI without a scheme`() = runTest { + val copier = DefaultNotificationSoundCopier(context) + val result = copier.copyToAppFiles("not-a-real-uri", NotificationSoundCopier.SoundSlot.Message) + assertThat(result).isEqualTo(NotificationSoundCopier.CopyResult.UnplayableSource) + } +} diff --git a/libraries/push/test/build.gradle.kts b/libraries/push/test/build.gradle.kts index 9dedeb3996..d125a2387e 100644 --- a/libraries/push/test/build.gradle.kts +++ b/libraries/push/test/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { api(projects.libraries.pushproviders.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.preferences.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.workmanager.api) implementation(projects.tests.testutils) diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationSoundUpdater.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationSoundUpdater.kt new file mode 100644 index 0000000000..1bbd410aec --- /dev/null +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationSoundUpdater.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.test.notifications + +import io.element.android.libraries.preferences.api.store.NotificationSound +import io.element.android.libraries.push.api.notifications.NotificationSoundUpdater +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeNotificationSoundUpdater( + private val recreateNoisyChannelLambda: (sound: NotificationSound, version: Int) -> Unit = { _, _ -> lambdaError() }, + private val recreateRingingCallChannelLambda: (sound: NotificationSound, version: Int) -> Unit = { _, _ -> lambdaError() }, + private val readNoisyChannelSoundLambda: () -> NotificationSound? = { null }, + private val readRingingCallChannelSoundLambda: () -> NotificationSound? = { null }, +) : NotificationSoundUpdater { + override fun recreateNoisyChannel(sound: NotificationSound, version: Int) { + recreateNoisyChannelLambda(sound, version) + } + + override fun recreateRingingCallChannel(sound: NotificationSound, version: Int) { + recreateRingingCallChannelLambda(sound, version) + } + + override suspend fun readNoisyChannelSound(): NotificationSound? = readNoisyChannelSoundLambda() + + override suspend fun readRingingCallChannelSound(): NotificationSound? = readRingingCallChannelSoundLambda() +} diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeSoundDisplayNameResolver.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeSoundDisplayNameResolver.kt new file mode 100644 index 0000000000..69bb77b65c --- /dev/null +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeSoundDisplayNameResolver.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.test.notifications + +import io.element.android.libraries.push.api.notifications.SoundDisplayNameResolver + +class FakeSoundDisplayNameResolver( + // Default resolves to a non-null sentinel so a default-constructed fake doesn't masquerade + // as "every Custom URI is unresolvable" — that would trip mid-session detection paths in + // every test that uses default fakes. Tests that need the unavailable case pass `{ null }`. + private val resolveLambda: suspend (uri: String) -> String? = { "FakeRingtoneTitle" }, +) : SoundDisplayNameResolver { + override suspend fun resolveCustomSoundTitle(uri: String): String? = resolveLambda(uri) +} diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/sound/FakeNotificationSoundCopier.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/sound/FakeNotificationSoundCopier.kt new file mode 100644 index 0000000000..9fe88b7718 --- /dev/null +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/sound/FakeNotificationSoundCopier.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.push.test.notifications.sound + +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.CopyResult +import io.element.android.libraries.push.api.notifications.sound.NotificationSoundCopier.SoundSlot +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeNotificationSoundCopier( + private val copyLambda: (String, SoundSlot) -> CopyResult = { _, _ -> lambdaError() }, + private val deleteStoredSoundForLambda: (SoundSlot) -> Unit = {}, +) : NotificationSoundCopier { + override suspend fun copyToAppFiles(sourceUriString: String, slot: SoundSlot): CopyResult = copyLambda(sourceUriString, slot) + + override suspend fun deleteStoredSoundFor(slot: SoundSlot) { + deleteStoredSoundForLambda(slot) + } +} diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 449fd0f352..88d8d712b0 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -480,10 +480,6 @@ Haluatko varmasti jatkaa?" "Vaihtoehdot" "Poista %1$s" "Asetukset" - - "%1$d aste" - "%1$d astetta" - "Kukaan ei jaa sijaintiaan" "Jaetaan reaaliaikaista sijaintia" 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 212b5eb79a..7d178cc105 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -480,10 +480,6 @@ Raison : %1$s." "Options" "Supprimer %1$s" "Paramètres" - - "%1$d degré" - "%1$d degrés" - "Personne ne partage sa position" "Partage de la position en direct" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index c32cbca475..ab5f64fc9e 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -471,11 +471,6 @@ "選択肢" "%1$s を削除" "設定" - "画像を左に回転" - - "%1$d°" - - "写真を編集" "誰も位置情報を共有していません" "ライブ位置情報を共有しています" 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 da3a5c2fea..7e0b78641e 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -489,13 +489,6 @@ Czy na pewno chcesz kontynuować?" "Opcje" "Usuń %1$s" "Ustawienia" - "Obróć obraz w lewo" - - "%1$d stopień" - "%1$d stopnie" - "%1$d stopni" - - "Edytuj zdjęcie" "Nikt nie udostępnia swojej lokalizacji" "Udostępnianie lokalizacji na żywo" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 1b50794182..e40d369f76 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -469,11 +469,6 @@ "选项" "移除 %1$s" "设置" - "向左旋转图像" - - "%1$d 度" - - "编辑照片" "目前无人分享其位置" "共享实时位置" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 0b9490ae59..d74f532484 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -481,12 +481,6 @@ Are you sure you want to continue?" "Options" "Remove %1$s" "Settings" - "Rotate the image to the left" - - "%1$d degree" - "%1$d degrees" - - "Edit photo" "Nobody is sharing their location" "Sharing live location" diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_0_en.png index d927ea14b0..7b985075fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01999b61120bb58f8cfa56a2f5b1b5573d2755fc5be85d3929237d6a61244ab0 -size 54308 +oid sha256:2dd82d156d36a7c7141fd5a4eafdbcbfe9aacc7a8dcede267fdfde272519bee6 +size 52088 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_14_en.png new file mode 100644 index 0000000000..29adf28369 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72acb2485a98126e53c27459d63fe7d3718ebc94215a597207ff820d4c9e45a7 +size 43439 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_15_en.png new file mode 100644 index 0000000000..b79295b20d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_15_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8b4f5fe2d2aa5459aac05180882b3aaa9fc9777f84f4c817c9e995049485946 +size 42052 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_16_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_16_en.png new file mode 100644 index 0000000000..f85174c9c8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba8f5f9d0ce23de5e6d57660887478b04851680d13a9f8f70d1488c557a25afc +size 43261 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_17_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_17_en.png new file mode 100644 index 0000000000..e4c7c476f5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dc8f7d0497632c723311c530d37807261686ed9fe4d7ff926c0a0fbf825cf23 +size 43179 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_18_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_18_en.png new file mode 100644 index 0000000000..e34ca68b36 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cc1821e0f114837943f9b9ff76f43a1bf871cc49a4eff704e8c87b50cc384ba +size 44736 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_19_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_19_en.png new file mode 100644 index 0000000000..e9127c1ccd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_19_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbf8bd63967dc3e0739ea2cc62c42fbcbe7d0a9cdf50b41f24c1ccab1f300eab +size 39442 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_1_en.png index 4302a4c031..e4c7c476f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52f7675544ba71ed73d2f4283287f46eaa0798d48993d7eb54fc28f87c89cf0e -size 43466 +oid sha256:3dc8f7d0497632c723311c530d37807261686ed9fe4d7ff926c0a0fbf825cf23 +size 43179 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_20_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_20_en.png new file mode 100644 index 0000000000..4c2b80b959 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_20_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0c31e3d7a0788e91287da4ed4f0b6e5e1ade8ce542392f4ba6029d524372c32 +size 41551 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_2_en.png index edcd62c95f..a35714aff4 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50cca191bf1c21a4d0f2765643712d0db6813dc23f2c4b70589ad65f2847b259 -size 40215 +oid sha256:176519a45ab97560dc2f8bf1416915759039616458f5fde22def7022595b4cc9 +size 39890 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_3_en.png index caf2a0b795..ce4c8a5f2a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c13b82c858490228a4f5568b7489115d916af067a8cf2e01e22e44f56cd6c3e -size 39566 +oid sha256:7efafe0931f350b3369a73fc4cb4156f641d894db5e939202ab9fec5cec8423b +size 39229 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_4_en.png index caf2a0b795..ce4c8a5f2a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c13b82c858490228a4f5568b7489115d916af067a8cf2e01e22e44f56cd6c3e -size 39566 +oid sha256:7efafe0931f350b3369a73fc4cb4156f641d894db5e939202ab9fec5cec8423b +size 39229 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_5_en.png index 4302a4c031..e4c7c476f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52f7675544ba71ed73d2f4283287f46eaa0798d48993d7eb54fc28f87c89cf0e -size 43466 +oid sha256:3dc8f7d0497632c723311c530d37807261686ed9fe4d7ff926c0a0fbf825cf23 +size 43179 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_6_en.png index 5b314cc55a..6ce1addb3a 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38839f3e1cc1ed7fd02695e1ad68e44692fc836ee3fad075ada9d500a94b2554 -size 39554 +oid sha256:73cf2629ea291b423460d8756814e97b2e8720a064aaf7fc9f3f3a7cf9a34862 +size 39186 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_7_en.png index b292bd42f5..e368d72b51 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc4c290503b6ae3157cf4169b50a8e00bae953d87b2d319389e8ab3decb9ca3 -size 43856 +oid sha256:efb91f3ae38c55d585d86d1c83f8c52e7244b62b64c82f51ad145ac3ec9841a7 +size 43493 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_8_en.png index 4302a4c031..e4c7c476f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52f7675544ba71ed73d2f4283287f46eaa0798d48993d7eb54fc28f87c89cf0e -size 43466 +oid sha256:3dc8f7d0497632c723311c530d37807261686ed9fe4d7ff926c0a0fbf825cf23 +size 43179 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_9_en.png index 4302a4c031..e4c7c476f5 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52f7675544ba71ed73d2f4283287f46eaa0798d48993d7eb54fc28f87c89cf0e -size 43466 +oid sha256:3dc8f7d0497632c723311c530d37807261686ed9fe4d7ff926c0a0fbf825cf23 +size 43179 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_0_en.png index 5c281630e3..1859be12da 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8a677a3dbb1cf6cbb306d3b1e4ba612469cba408815320361365d74e188cf74 -size 52703 +oid sha256:5d921332fe1eb11a80750ed8934f3d386fa267e180bbd29960a750181bddcb49 +size 50570 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_14_en.png new file mode 100644 index 0000000000..f300562d3c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a189eca358d0bf0c2ed4f107252b667d996e560822ea8e39cf776511ebb0fde4 +size 41989 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_15_en.png new file mode 100644 index 0000000000..28dad18993 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_15_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019e7a646c19f2056873f186e8c7ee73127e79468e5149034e85455460b689be +size 40637 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_16_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_16_en.png new file mode 100644 index 0000000000..16bbe20275 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a9585f68e045397a46c8f368e4affa1e9fde985bf415014403b91e73de6eb9e +size 41713 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_17_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_17_en.png new file mode 100644 index 0000000000..d6ac948791 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca8ace95c2cb039826be9e39da427eebada1a8f6f234d6ae82c9e2a69f9a2c8e +size 41760 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_18_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_18_en.png new file mode 100644 index 0000000000..0e5833c48b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43d661bd16a8343e1750faeed957fe5490cee65d0bcc033ecef6344c7ee85fba +size 42104 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_19_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_19_en.png new file mode 100644 index 0000000000..f169d5f9d8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_19_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cacac247776f92bf1aeaed8b0b11a2283d27e51c6999f4fcd08b62b8aae4c3d7 +size 36997 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_1_en.png index 05b8276ace..d6ac948791 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d92a5ffd43eac457c3384cfc22b9b29fbf8c21532fc16892258140c557788bd2 -size 41872 +oid sha256:ca8ace95c2cb039826be9e39da427eebada1a8f6f234d6ae82c9e2a69f9a2c8e +size 41760 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_20_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_20_en.png new file mode 100644 index 0000000000..03061e5717 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_20_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6685d37388f89b1a6f18a66deb1a7a35f23a88c2bd64e3d4b980f71cd9cc5aa +size 39185 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_2_en.png index 56334c1e2e..d1678d364f 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd1599665f32844b4757a631542351303b7d4f4a34f32640ee9f60ad38089cab -size 38707 +oid sha256:50d4e41bfed974afdcdb1864f95e9b5ea26675f67c16cd17d82c431547be4b5b +size 38565 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_3_en.png index c7e3274c19..a6c678cb89 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea8532bc891ce639c492ed7d6edaaadefc2b99dd3b68873a575858b03b6fcbfe -size 36998 +oid sha256:6c31c52efbf995bcc237a309772be0c70221db7d30dec38c734bf01f75bb1a97 +size 36856 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_4_en.png index c7e3274c19..a6c678cb89 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea8532bc891ce639c492ed7d6edaaadefc2b99dd3b68873a575858b03b6fcbfe -size 36998 +oid sha256:6c31c52efbf995bcc237a309772be0c70221db7d30dec38c734bf01f75bb1a97 +size 36856 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_5_en.png index 05b8276ace..d6ac948791 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d92a5ffd43eac457c3384cfc22b9b29fbf8c21532fc16892258140c557788bd2 -size 41872 +oid sha256:ca8ace95c2cb039826be9e39da427eebada1a8f6f234d6ae82c9e2a69f9a2c8e +size 41760 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_6_en.png index 1f3b3e2b91..aa887109d2 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68d4a4cd27afda0d23438451124ef997dadd116e1ea5db4b5a6af0045d95ba2b -size 36998 +oid sha256:64b969354abd79e131dc6bf644f48e2218216fd4a6b8e93527f52defc8b2cb38 +size 36836 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_7_en.png index bbc69da0c3..4e38e5b2bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:784b744139daa4216d4137aaaca3a4f1a86ef7297ce2996bb9e9eb78c8aa93fb -size 41201 +oid sha256:b07d98cdb41d4daf9bec18285588ad5bf99992a52ef27f2da66545734b8db843 +size 41036 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_8_en.png index 05b8276ace..d6ac948791 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d92a5ffd43eac457c3384cfc22b9b29fbf8c21532fc16892258140c557788bd2 -size 41872 +oid sha256:ca8ace95c2cb039826be9e39da427eebada1a8f6f234d6ae82c9e2a69f9a2c8e +size 41760 diff --git a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_9_en.png index 05b8276ace..d6ac948791 100644 --- a/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.preferences.impl.notifications_NotificationSettingsView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d92a5ffd43eac457c3384cfc22b9b29fbf8c21532fc16892258140c557788bd2 -size 41872 +oid sha256:ca8ace95c2cb039826be9e39da427eebada1a8f6f234d6ae82c9e2a69f9a2c8e +size 41760 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 9936341cee..64976003d5 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -330,6 +330,7 @@ "screen\\.advanced_settings\\..*", "screen_edit_profile_.*", "screen_notification_settings_.*", + "screen\\.notification_settings\\..*", "screen_blocked_users_.*", "full_screen_intent_banner_.*", "troubleshoot_notifications_entry_point_.*", From e1aab57b964865b115524187d2dad2e4bdda49eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 15:07:36 +0000 Subject: [PATCH 273/471] Update tspascoal/get-user-teams-membership action to v4.0.2 --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index f0c8fd1e6f..e24847280e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Check membership if: github.event.pull_request.user.login != 'renovate[bot]' - uses: tspascoal/get-user-teams-membership@818140d631d5f29f26b151afbe4179f87d9ceb5e # v4.0.1 + uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2 id: teams with: username: ${{ github.event.pull_request.user.login }} From 6fd602a08fbbd9c269fde0e53ab9d9ff7e47a4eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 15:07:59 +0000 Subject: [PATCH 274/471] Update actions/stale action to v10.3.0 --- .github/workflows/stale-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 1958e80083..622fecc403 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -12,7 +12,7 @@ jobs: permissions: issues: write steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: only-labels: "X-Needs-Info" days-before-issue-stale: 30 From 510588b93eac98658f757b2ab1020abdd5638f26 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 28 May 2026 18:19:48 +0200 Subject: [PATCH 275/471] Use simple custom LocationTrackingEffect waiting for a fix from maplibre --- .../common/ui/SimpleLocationTrackingEffect.kt | 52 +++++++++++++++++++ .../impl/common/ui/UserLocationPuck.kt | 37 +++++-------- .../location/impl/show/ShowLocationView.kt | 4 +- 3 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt new file mode 100644 index 0000000000..7a7a1551cf --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.snapshotFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import org.maplibre.compose.location.Location +import org.maplibre.compose.location.UserLocationState +import kotlin.math.abs + +/** + * Drop-in replacement for the library's LocationTrackingEffect. + * TODO remove once https://github.com/maplibre/maplibre-compose/issues/808 is fixed + */ +@Composable +internal fun SimpleLocationTrackingEffect( + locationState: UserLocationState, + enabled: Boolean = true, + precision: Double = 0.00001, + onLocationChange: suspend (Location?) -> Unit, +) { + val latestOnLocationChange by rememberUpdatedState(onLocationChange) + + LaunchedEffect(locationState, enabled) { + if (!enabled) return@LaunchedEffect + val locationStateFlow = snapshotFlow { locationState.location } + locationStateFlow + .distinctUntilChanged { oldLocation, newLocation -> + if (oldLocation != null && newLocation != null) { + when { + abs(oldLocation.position.value.latitude - newLocation.position.value.latitude) >= precision -> false + abs(oldLocation.position.value.longitude - newLocation.position.value.longitude) >= precision -> false + else -> true + } + } else { + false + } + } + .collect { location -> + latestOnLocationChange(location) + } + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt index 6f47903c9c..7bad736911 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt @@ -13,19 +13,15 @@ import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.impl.common.MapDefaults -import kotlinx.coroutines.flow.SharingStarted import org.maplibre.compose.camera.CameraState import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.LocationPuck import org.maplibre.compose.location.LocationPuckColors import org.maplibre.compose.location.LocationPuckSizes -import org.maplibre.compose.location.LocationTrackingEffect import org.maplibre.compose.location.UserLocationState import org.maplibre.compose.location.rememberAndroidLocationProvider import org.maplibre.compose.location.rememberNullLocationProvider import org.maplibre.compose.location.rememberUserLocationState -import org.maplibre.spatialk.units.Bearing -import org.maplibre.spatialk.units.extensions.inDegrees import org.maplibre.spatialk.units.extensions.meters import kotlin.time.Duration.Companion.seconds @@ -35,28 +31,24 @@ fun UserLocationPuck( locationState: UserLocationState, trackUserLocation: Boolean, ) { + SimpleLocationTrackingEffect( + locationState = locationState, + enabled = trackUserLocation, + ) { currentLocation -> + val target = currentLocation?.position?.value ?: cameraState.position.target + val newPosition = cameraState.position.copy( + target = target, + // Force pointing to NORTH + bearing = 0.0, + zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) + ) + cameraState.animateTo(newPosition) + } val location = locationState.location if (location != null) { - // Moved inside this block so it correctly tracks the updated locationState value - LocationTrackingEffect( - locationState = locationState, - enabled = trackUserLocation, - ) { - val newTarget = currentLocation.location?.position?.value - val newBearing = currentLocation.orientation?.orientation?.value?.clockwiseRotationTo(Bearing.North)?.inDegrees - if (newTarget != null || newBearing != null) { - val finalPosition = cameraState.position.copy( - target = newTarget ?: cameraState.position.target, - bearing = newBearing ?: cameraState.position.bearing, - zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) - ) - cameraState.animateTo(finalPosition) - } - } - LocationPuck( idPrefix = "user-location", - location = locationState.location, + location = location, cameraState = cameraState, accuracyThreshold = Float.POSITIVE_INFINITY, showBearingAccuracy = false, @@ -85,7 +77,6 @@ fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState updateInterval = 5.seconds, desiredAccuracy = DesiredAccuracy.High, minDistance = 5.meters, - sharingStarted = SharingStarted.Eagerly, ) } return rememberUserLocationState(locationProvider) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 6766fa6424..eb2cae0539 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -153,7 +153,9 @@ fun ShowLocationView( val position = CameraPosition( padding = sheetPaddings, target = Position(locationShare.location.lon, locationShare.location.lat), - zoom = MapDefaults.DEFAULT_ZOOM + // Force pointing to NORTH + bearing = 0.0, + zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM), ) coroutineScope.launch { cameraState.animateTo(finalPosition = position) From c5f9b554338912a862cc75a3bad6275b433dd995 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 28 May 2026 18:20:22 +0200 Subject: [PATCH 276/471] Keep accuracy from position (no default) --- .../location/impl/live/service/LiveLocationSharingService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt index aba45f30c1..a926f1255b 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -104,7 +104,7 @@ class LiveLocationSharingService : Service() { ApiLocation( lat = location.position.value.latitude, lon = location.position.value.longitude, - accuracy = location.position.accuracy?.inMeters?.toFloat() ?: 0f, + accuracy = location.position.accuracy?.inMeters?.toFloat(), ) } .onEach(coordinator::dispatch) From e0cda170a9f5f0b4a74ef943ba66c93132417477 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 29 May 2026 10:18:12 +0300 Subject: [PATCH 277/471] Do not hide own media (#6898) --- .../messages/impl/MessagesPresenter.kt | 2 +- .../pinned/list/PinnedMessagesListView.kt | 2 +- .../TimelineItemGroupedEventsRow.kt | 4 +- .../timeline/components/TimelineItemRow.kt | 2 +- .../impl/timeline/protection/TimelineItem.kt | 46 +++++++++++-------- .../protection/TimelineProtectionState.kt | 10 ++-- .../protection/TimelineProtectionStateTest.kt | 15 ++++++ 7 files changed, 53 insertions(+), 28 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 6754d703a3..d3737f5fae 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 @@ -550,7 +550,7 @@ class MessagesPresenter( val replyToDetails = loadReplyDetails(targetEvent.eventId).map(permalinkParser) val composerMode = MessageComposerMode.Reply( replyToDetails = replyToDetails, - hideImage = timelineProtectionState.hideMediaContent(targetEvent.eventId), + hideImage = timelineProtectionState.hideMediaContent(targetEvent.eventId, targetEvent.isMine), ) composerState.eventSink( MessageComposerEvent.SetMode(composerMode) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 7190aa174f..9811b07c02 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -284,7 +284,7 @@ private fun TimelineItemEventContentViewWrapper( } else { TimelineItemEventContentView( content = event.content, - hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), + hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId, event.isMine), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index df2b9d8691..12f7a52f99 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -63,7 +63,7 @@ fun TimelineItemGroupedEventsRow( { event, contentModifier, onContentLayoutChange -> TimelineItemEventContentView( content = event.content, - hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), + hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId, event.isMine), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, @@ -136,7 +136,7 @@ private fun TimelineItemGroupedEventsRowContent( { event, contentModifier, onContentLayoutChange -> TimelineItemEventContentView( content = event.content, - hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), + hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId, event.isMine), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, onLinkLongClick = onLinkLongClick, 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 a02413d534..2b72be722d 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 @@ -78,7 +78,7 @@ internal fun TimelineItemRow( { event, contentModifier, onContentLayoutChange -> TimelineItemEventContentView( content = event.content, - hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), + hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId, event.isMine), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onContentClick = { onContentClick(event) }, onLongClick = { onLongClick(event) }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineItem.kt index 5a5363f0c6..fcaa28a108 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineItem.kt @@ -34,26 +34,32 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt */ fun TimelineItem.mustBeProtected(): Boolean { return when (this) { - is TimelineItem.Event -> when (content) { - is TimelineItemImageContent, - is TimelineItemVideoContent, - is TimelineItemStickerContent -> true - is TimelineItemAudioContent, - is TimelineItemRtcNotificationContent, - is TimelineItemEncryptedContent, - is TimelineItemFileContent, - TimelineItemLegacyCallInviteContent, - is TimelineItemLocationContent, - is TimelineItemPollContent, - TimelineItemRedactedContent, - is TimelineItemProfileChangeContent, - is TimelineItemRoomMembershipContent, - is TimelineItemStateEventContent, - is TimelineItemEmoteContent, - is TimelineItemNoticeContent, - is TimelineItemTextContent, - TimelineItemUnknownContent, - is TimelineItemVoiceContent -> false + is TimelineItem.Event -> { + if (isMine) { + false + } else { + when (content) { + is TimelineItemImageContent, + is TimelineItemVideoContent, + is TimelineItemStickerContent -> true + is TimelineItemAudioContent, + is TimelineItemRtcNotificationContent, + is TimelineItemEncryptedContent, + is TimelineItemFileContent, + TimelineItemLegacyCallInviteContent, + is TimelineItemLocationContent, + is TimelineItemPollContent, + TimelineItemRedactedContent, + is TimelineItemProfileChangeContent, + is TimelineItemRoomMembershipContent, + is TimelineItemStateEventContent, + is TimelineItemEmoteContent, + is TimelineItemNoticeContent, + is TimelineItemTextContent, + TimelineItemUnknownContent, + is TimelineItemVoiceContent -> false + } + } } is TimelineItem.Virtual -> false is TimelineItem.GroupedEvents -> false diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionState.kt index 169e5ba7f4..df31c205e9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionState.kt @@ -16,9 +16,13 @@ data class TimelineProtectionState( val protectionState: ProtectionState, val eventSink: (TimelineProtectionEvent) -> Unit, ) { - fun hideMediaContent(eventId: EventId?) = when (protectionState) { - is ProtectionState.RenderAll -> false - is ProtectionState.RenderOnly -> eventId !in protectionState.eventIds + fun hideMediaContent(eventId: EventId?, isMine: Boolean = false) = if (isMine) { + false + } else { + when (protectionState) { + is ProtectionState.RenderAll -> false + is ProtectionState.RenderOnly -> eventId !in protectionState.eventIds + } } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStateTest.kt index 645039b30c..c5709588f0 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStateTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionStateTest.kt @@ -42,4 +42,19 @@ class TimelineProtectionStateTest { assertThat(sut.hideMediaContent(AN_EVENT_ID)).isFalse() assertThat(sut.hideMediaContent(AN_EVENT_ID_2)).isTrue() } + + @Test + fun `when isMine is true, hideMediaContent always returns false regardless of state`() { + val sutRenderAll = aTimelineProtectionState( + protectionState = ProtectionState.RenderAll + ) + assertThat(sutRenderAll.hideMediaContent(null, isMine = true)).isFalse() + assertThat(sutRenderAll.hideMediaContent(AN_EVENT_ID, isMine = true)).isFalse() + + val sutRenderOnly = aTimelineProtectionState( + protectionState = ProtectionState.RenderOnly(persistentSetOf()) + ) + assertThat(sutRenderOnly.hideMediaContent(null, isMine = true)).isFalse() + assertThat(sutRenderOnly.hideMediaContent(AN_EVENT_ID, isMine = true)).isFalse() + } } From 64b6a1523d05ee8138dc7bb5fdc07051cfcdbfb5 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 29 May 2026 10:56:18 +0000 Subject: [PATCH 278/471] Revert screen recomposition fixes --- .../impl/timeline/TimelinePresenter.kt | 27 ++++--------- .../factories/TimelineItemsFactory.kt | 39 ------------------- 2 files changed, 8 insertions(+), 58 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 3af7326ff5..0b99c45e06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -51,7 +51,6 @@ import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState import io.element.android.libraries.matrix.api.room.roomMembers -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin @@ -255,25 +254,15 @@ class TimelinePresenter( } .launchIn(this) - var previousItems: List? = null combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState -> - val roomMembers = membersState.roomMembers().orEmpty() - if (previousItems !== items) { - previousItems = items - val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems) - val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items") - transaction?.putExtraData(AnalyticsUserData.TIMELINE_ITEM_COUNT, items.count().toString()) - timelineItemsFactory.replaceWith( - timelineItems = items, - roomMembers = roomMembers - ) - transaction?.finish() - } else { - timelineItemsFactory.updateRoomMembers( - timelineItems = items, - roomMembers = roomMembers - ) - } + val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems) + val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items") + transaction?.putExtraData(AnalyticsUserData.TIMELINE_ITEM_COUNT, items.count().toString()) + timelineItemsFactory.replaceWith( + timelineItems = items, + roomMembers = membersState.roomMembers().orEmpty() + ) + transaction?.finish() items } .onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index d1ec5b648a..dc8bdddc92 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -72,45 +72,6 @@ class TimelineItemsFactory( } } - /** - * Lightweight update that only refreshes member-derived data (e.g., read receipt display names) - * on cached items without rebuilding the diff cache or creating new items. - * Skips emission if no cached items have member-dependent state. - */ - suspend fun updateRoomMembers( - timelineItems: List, - roomMembers: List, - ) = withContext(dispatchers.computation) { - lock.withLock { - var hasUpdates = false - val updatedStates = ArrayList() - for (index in diffCache.indices().reversed()) { - val cacheItem = diffCache.get(index) - if (cacheItem is TimelineItem.Event && roomMembers.isNotEmpty()) { - val updatedItem = eventItemFactory.update( - timelineItem = cacheItem, - receivedMatrixTimelineItem = timelineItems[index] as MatrixTimelineItem.Event, - roomMembers = roomMembers - ) - diffCache[index] = updatedItem - hasUpdates = true - updatedStates.add(updatedItem) - } else if (cacheItem != null) { - updatedStates.add(cacheItem) - } else { - buildAndCacheItem(timelineItems, index, roomMembers)?.also { timelineItemState -> - updatedStates.add(timelineItemState) - } - } - } - if (hasUpdates) { - val result = timelineItemGrouper.group(updatedStates).toImmutableList() - val filteredResult = filterEmptyDaySeparators(result) - _timelineItems.emit(filteredResult) - } - } - } - private suspend fun buildAndEmitTimelineItemStates( timelineItems: List, roomMembers: List, From a8e1274786c19230e989c9bca76f23222e3ae4cc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 29 May 2026 15:01:45 +0200 Subject: [PATCH 279/471] Rename parameter `onSoundPicked` to `onSoundPick`. --- .../impl/notifications/NotificationSettingsView.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index 5652276c23..2009970448 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -286,7 +286,7 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { type = RingtoneManager.TYPE_NOTIFICATION, current = state.messageSound.sound, defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI, - onSoundPicked = { sound -> state.eventSink(NotificationSettingsEvents.SetMessageSound(sound)) }, + onSoundPick = { sound -> state.eventSink(NotificationSettingsEvents.SetMessageSound(sound)) }, ) // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only // increments fired by LaunchMessageSoundPicker should launch it. @@ -313,7 +313,7 @@ private fun SoundsPreferenceCategory(state: NotificationSettingsState) { type = RingtoneManager.TYPE_RINGTONE, current = state.callRingtone.sound, defaultUri = Settings.System.DEFAULT_RINGTONE_URI, - onSoundPicked = { sound -> state.eventSink(NotificationSettingsEvents.SetCallRingtone(sound)) }, + onSoundPick = { sound -> state.eventSink(NotificationSettingsEvents.SetCallRingtone(sound)) }, ) // Skip the initial 0 emission so the picker doesn't auto-open on screen entry; only // increments fired by LaunchCallRingtonePicker should launch it. @@ -441,7 +441,7 @@ private fun rememberSoundPickerOnClick( type: Int, current: NotificationSound, defaultUri: Uri, - onSoundPicked: (NotificationSound) -> Unit, + onSoundPick: (NotificationSound) -> Unit, ): () -> Unit { // Paparazzi previews don't provide a LocalActivityResultRegistryOwner, which // rememberLauncherForActivityResult requires. Skip the launcher in inspection mode and @@ -453,7 +453,7 @@ private fun rememberSoundPickerOnClick( if (result.resultCode == Activity.RESULT_OK) { val sound = result.data?.toPickedNotificationSound(defaultUri) if (sound != null) { - onSoundPicked(sound) + onSoundPick(sound) } } } From 156473636640aa2099399de6bc1ef6add7cfd055 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 29 May 2026 14:26:45 +0200 Subject: [PATCH 280/471] Report problem: add an optional TextField to provide a GitHub issue number. --- .../rageshake/api/reporter/BugReporter.kt | 2 ++ .../impl/bugreport/BugReportEvents.kt | 1 + .../impl/bugreport/BugReportPresenter.kt | 6 +++- .../impl/bugreport/BugReportState.kt | 2 ++ .../rageshake/impl/bugreport/BugReportView.kt | 35 +++++++++++++++++++ .../impl/reporter/DefaultBugReporter.kt | 4 +++ .../impl/src/main/res/values/localazy.xml | 2 ++ .../impl/bugreport/BugReportPresenterTest.kt | 12 +++++++ .../impl/bugreport/FakeBugReporter.kt | 1 + 9 files changed, 64 insertions(+), 1 deletion(-) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt index 79b05ea32e..631a51a686 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt @@ -20,6 +20,7 @@ interface BugReporter { * @param problemDescription the bug description * @param canContact true if the user opt in to be contacted directly * @param sendPushRules true to include the push rules + * @param ghIssueNumber it not null, the GitHub issue number to link the bug report to. * @param listener the listener */ suspend fun sendBugReport( @@ -29,6 +30,7 @@ interface BugReporter { problemDescription: String, canContact: Boolean = false, sendPushRules: Boolean = false, + ghIssueNumber: Int? = null, listener: BugReporterListener ) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt index 90751873dd..94897d1bad 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportEvents.kt @@ -18,4 +18,5 @@ sealed interface BugReportEvents { data class SetCanContact(val canContact: Boolean) : BugReportEvents data class SetSendScreenshot(val sendScreenshot: Boolean) : BugReportEvents data class SetSendPushRules(val sendPushRules: Boolean) : BugReportEvents + data class SetGhIssueNumber(val ghIssueNumber: Int?) : BugReportEvents } 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 4985f9b30e..85731ae959 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 @@ -113,6 +113,9 @@ class BugReportPresenter( sendingProgress.floatValue = 0f sendingAction.value = AsyncAction.Uninitialized } + is BugReportEvents.SetGhIssueNumber -> updateFormState(formState) { + copy(ghIssueNumber = event.ghIssueNumber) + } } } @@ -142,7 +145,8 @@ class BugReportPresenter( problemDescription = formState.description, canContact = formState.canContact, sendPushRules = formState.sendPushRules, - listener = listener + ghIssueNumber = formState.ghIssueNumber, + listener = listener, ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt index 65cc055ec9..8e07b3c4e8 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportState.kt @@ -32,6 +32,7 @@ data class BugReportFormState( val canContact: Boolean, val sendScreenshot: Boolean, val sendPushRules: Boolean, + val ghIssueNumber: Int?, ) : Parcelable { companion object { val Default = BugReportFormState( @@ -40,6 +41,7 @@ data class BugReportFormState( canContact = false, sendScreenshot = false, sendPushRules = false, + ghIssueNumber = null, ) } } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index c2c34835c9..0e6241c1f3 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -155,6 +155,41 @@ fun BugReportView( title = stringResource(R.string.screen_bug_report_send_notification_settings_title), subtitle = stringResource(R.string.screen_bug_report_send_notification_settings_description), ) + PreferenceRow { + var ghIssueNumberState by textFieldState( + stateValue = state.formState.ghIssueNumber?.toString() ?: "" + ) + TextField( + value = ghIssueNumberState, + modifier = Modifier + .fillMaxWidth() + .onTabOrEnterKeyFocusNext(LocalFocusManager.current), + enabled = isFormEnabled, + label = stringResource(id = R.string.screen_bug_report_github_issue_label), + placeholder = "1234", + supportingText = stringResource(id = R.string.screen_bug_report_github_issue_description), + onValueChange = { + if (it.isEmpty()) { + ghIssueNumberState = "" + eventSink(BugReportEvents.SetGhIssueNumber(null)) + } else { + val number = it.toIntOrNull()?.takeIf { ghInt -> ghInt in 1..99_999 } + number?.let { ghIssueNumber -> + ghIssueNumberState = ghIssueNumber.toString() + eventSink(BugReportEvents.SetGhIssueNumber(ghIssueNumber)) + } + } + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + keyboardActions = KeyboardActions(onNext = { + keyboardController?.hide() + }), + singleLine = true, + ) + } // Submit PreferenceRow { Button( diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 2568a6fba9..735bb1cea0 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -123,6 +123,7 @@ class DefaultBugReporter( problemDescription: String, canContact: Boolean, sendPushRules: Boolean, + ghIssueNumber: Int?, listener: BugReporterListener, ) { val url = bugReporterUrlProvider.provide().first() @@ -144,6 +145,9 @@ class DefaultBugReporter( val crashCallStack = crashDataStore.crashInfo().first() val bugDescription = buildString { append(problemDescription) + ghIssueNumber?.let { + append("\n\nhttps://github.com/element-hq/element-x-android/issues/$it") + } if (crashCallStack.isNotEmpty() && withCrashLogs) { append("\n\n\n\n--------------------------------- crash call stack ---------------------------------\n") append(crashCallStack) diff --git a/features/rageshake/impl/src/main/res/values/localazy.xml b/features/rageshake/impl/src/main/res/values/localazy.xml index 85af2af4a7..d1dc52f3e4 100644 --- a/features/rageshake/impl/src/main/res/values/localazy.xml +++ b/features/rageshake/impl/src/main/res/values/localazy.xml @@ -8,6 +8,8 @@ "Describe the problem…" "If possible, please write the description in English." "The description is too short, please provide more details about what happened. Thanks!" + "You can enter the number of an associated GitHub issue, if any." + "GitHub issue" "Send crash logs" "Allow logs" "Your logs are excessively large so cannot be included in this report, please send them to us another way." 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 148c375e25..afbcc939a6 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 @@ -107,6 +107,18 @@ class BugReportPresenterTest { } } + @Test + fun `present - set GitHub issue number`() = runTest { + val presenter = createPresenter() + presenter.test { + val initialState = awaitItem() + initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(1)) + assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = 1)) + initialState.eventSink.invoke(BugReportEvents.SetGhIssueNumber(null)) + assertThat(awaitItem().formState).isEqualTo(BugReportFormState.Default.copy(ghIssueNumber = null)) + } + } + @Test fun `present - reset all`() = runTest { val presenter = createPresenter( diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt index 36cc185e86..659145caa9 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt @@ -28,6 +28,7 @@ class FakeBugReporter(val mode: Mode = Mode.Success) : BugReporter { problemDescription: String, canContact: Boolean, sendPushRules: Boolean, + ghIssueNumber: Int?, listener: BugReporterListener, ) { delay(100) From 488ef54cbc3ef59fcfe8a90c71d25be1b69f4178 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 29 May 2026 15:39:16 +0200 Subject: [PATCH 281/471] Add leading icon `#`. --- .../features/rageshake/impl/bugreport/BugReportView.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 0e6241c1f3..36b407a669 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import coil3.request.CachePolicy import coil3.request.ImageRequest +import io.element.android.compound.theme.ElementTheme import io.element.android.features.rageshake.impl.R import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.async.AsyncActionView @@ -168,6 +169,13 @@ fun BugReportView( label = stringResource(id = R.string.screen_bug_report_github_issue_label), placeholder = "1234", supportingText = stringResource(id = R.string.screen_bug_report_github_issue_description), + leadingIcon = { + Text( + text = "#", + style = ElementTheme.typography.fontBodyLgMedium, + color = ElementTheme.colors.textSecondary, + ) + }, onValueChange = { if (it.isEmpty()) { ghIssueNumberState = "" From 24c11e1b0f31c567f4a105dc96851cce89617d66 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 29 May 2026 15:59:54 +0200 Subject: [PATCH 282/471] Add Stefan's etiquette to the project. (#6909) --- .github/pull_request_template.md | 1 + AGENTS.md | 13 ++++++++----- CONTRIBUTING.md | 25 +++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 95f322a2e4..9ed1919e0e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -52,6 +52,7 @@ Uncomment this markdown table below and edit the last line `|||`: +- [ ] I am aware of the [etiquette](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#etiquette). - This PR was made with the help of AI: - [ ] Yes. In this case, please request a review by Copilot. - [ ] No. diff --git a/AGENTS.md b/AGENTS.md index 454c347a9f..d206be98c6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,11 +44,14 @@ PRs must meet these rules. ## Pull Request Guidelines -- Use sentence-style commit/PR messages (no conventional commits). -- Apply exactly **one** `PR-` label for changelog categorization. -- PR title = changelog entry — make it descriptive; no "Fixes #…" prefixes. -- Include screenshots or screen recordings for any UI changes. -- Keep PRs focused; split changes over 1000 lines. +- Sentence-style titles (no conventional commits). +- Exactly one `pr-` label (see `.github/release.yml`). +- Title = changelog entry — descriptive, no "Fixes #…". +- Leave description template for the developer. Redirect them to the [contributing etiquette](CONTRIBUTING.md#etiquette). +- Screenshots/videos for visual changes. +- 500 additions max — split large changes. +- Commits need a title and description; no tiny or massive commits. +- No history rewrites. --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0126d89a9c..ec131aa65f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,8 @@ * [I want to help translating Element](#i-want-to-help-translating-element) * [I want to fix a bug](#i-want-to-fix-a-bug) * [I want to add a new feature or enhancement](#i-want-to-add-a-new-feature-or-enhancement) -* [Developer onboarding](#developer-onboarding) + * [Etiquette](#etiquette) + * [Developer onboarding](#developer-onboarding) * [Submitting the PRs](#submitting-the-prs) * [Android Studio settings](#android-studio-settings) * [Compilation](#compilation) @@ -63,7 +64,27 @@ Once we know that you want to contribute and have confirmed that the new feature Only once all of the above is met should you open a PR with your proposed changes. -## Developer onboarding +### Etiquette + +* As stated above all significant changes should be communicated through an issue before opening a PR +* We are happy to receive contributions but features always require maintenance, so depending on the change we might not be willing to accept it +* We are also fine with AI led contributions within reasonable bounds + * You are completely responsible for the quality of the PR + * If the PR doesn't show minimal effort (code does not compile, code does not work as expected, etc.) on your part it will be closed + * Please write the description yourself, we don't have the bandwidth to read LLM essays. The code needs to speak for itself. +* We use git for version control and GitHub for reviews, so in order to make everybody's life easier please: + * Keep the existing pull request template + * Don't submit large PRs, especially if not previously talked about. Anything above 200 lines is large (excluding generated code e.g. tests, translations, mocks) + * Please don't open unfinished PRs and expect us to fill in the details + * If you would like our opinion/direction on unfinished code please link your branch or idea in the ticket + * Please limit the number of commits in a single PR. We are perfectly happy with splitting work across multiple sessions as long as they're logically independant and show promise of progress (ideally expressed through a ticket) + * Each and every commit should stand on its own, clearly explaining what it does and why +* Once a PR goes into review please don't rewrite the history unless agreed so with the reviewer. + * Tweaks and fixes following review can be directly committed (to be interactively rebased later) or as fixups + +*The reviewer's response time will generally match yours. Switching contexts is very hard so please act accordingly. You are responsible for making the reviewers job enjoyable!* + +### Developer onboarding For a detailed overview of the project, see [Developer Onboarding](./docs/_developer_onboarding.md). From d0db687a0680873f58552a96969dc97d3cfdb003 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 29 May 2026 14:13:04 +0000 Subject: [PATCH 283/471] Update screenshots --- ...eatures.rageshake.impl.bugreport_BugReportViewDay_0_en.png | 4 ++-- ...eatures.rageshake.impl.bugreport_BugReportViewDay_1_en.png | 4 ++-- ...eatures.rageshake.impl.bugreport_BugReportViewDay_2_en.png | 4 ++-- ...eatures.rageshake.impl.bugreport_BugReportViewDay_3_en.png | 4 ++-- ...eatures.rageshake.impl.bugreport_BugReportViewDay_4_en.png | 4 ++-- ...tures.rageshake.impl.bugreport_BugReportViewNight_0_en.png | 4 ++-- ...tures.rageshake.impl.bugreport_BugReportViewNight_1_en.png | 4 ++-- ...tures.rageshake.impl.bugreport_BugReportViewNight_2_en.png | 4 ++-- ...tures.rageshake.impl.bugreport_BugReportViewNight_3_en.png | 4 ++-- ...tures.rageshake.impl.bugreport_BugReportViewNight_4_en.png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_0_en.png index b81b524b86..0599f2300a 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a10513e98757e17cc3ffb8302807896839a78d036331e79bb207686f3f9db86 -size 47842 +oid sha256:a4b243025f40d5d4a91e32d59a6cac9ec21a273aaddbfac020cb26b3dc86c906 +size 53339 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_1_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_1_en.png index 0700c94bb7..9e2407229c 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05efb0fa45bb6440e75e9280b219cfdac83c372efe9e6d6254b4bbe516f8b998 -size 112842 +oid sha256:e98722e0c21fe6b509b2bdf4a9ce7f78a1c83b606411329d2fd97a676fc4bed3 +size 113728 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_2_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_2_en.png index 61e347497e..bfd0f366be 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:833d21633cb0f153b660112fcfd0923c588cd26b64005403a007d7782ecfe5d5 -size 45138 +oid sha256:44eed3e8a093977f51d706acbec65cf29d592dcf0af3cdc9fce85d2b8e570eb9 +size 51038 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_3_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_3_en.png index b81b524b86..0599f2300a 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a10513e98757e17cc3ffb8302807896839a78d036331e79bb207686f3f9db86 -size 47842 +oid sha256:a4b243025f40d5d4a91e32d59a6cac9ec21a273aaddbfac020cb26b3dc86c906 +size 53339 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_4_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_4_en.png index 948604dd84..1a5d877a24 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewDay_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebe22ec065cc6dff2133dcc573a82e003d9061ae0446c2d8c13f4e1fba1f3c19 -size 37056 +oid sha256:781287977ff2637fe6479c3653270783c4374bc10fffb91da8b45d4e4f588a98 +size 41744 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_0_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_0_en.png index 9b379990c3..3fea4591cd 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f647e92bf813ead7affcdb6ad07076fe9d739f6a4c0016b9787912f032e27476 -size 46567 +oid sha256:f690c34f97eec23f01bfdc3c2606a229bb12abb91fe86fb79c89da79b08480a0 +size 52049 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_1_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_1_en.png index fcdfe6ba7d..957e19a4ce 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdfbb9f70a97724a1b6687ad4f199efc2c9bcc5f29fe9475a17ee6e52e5ecf8e -size 110829 +oid sha256:751b442e5aecd9d3938817f8a1af11b7cc1d250c1166c9e8bd3154a85268471d +size 111765 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_2_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_2_en.png index 6760c96a86..390d0faf7e 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1cfab4886243e6c914f466f5f127ec0347b0ab30ee43fb95c7bc82d2b16325d5 -size 43727 +oid sha256:0719e803112640729fe2b59c40373343db3e9f2e8dded9258d4b6c44547f8fd1 +size 49420 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_3_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_3_en.png index 9b379990c3..3fea4591cd 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f647e92bf813ead7affcdb6ad07076fe9d739f6a4c0016b9787912f032e27476 -size 46567 +oid sha256:f690c34f97eec23f01bfdc3c2606a229bb12abb91fe86fb79c89da79b08480a0 +size 52049 diff --git a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_4_en.png b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_4_en.png index 6b7ad4a825..4fd1ecbc0e 100644 --- a/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.rageshake.impl.bugreport_BugReportViewNight_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c24515cc18b3c5cc52f2acfcee4faa65c02d8d5c8be847d4b1ec5671a858210 -size 35201 +oid sha256:1cb10ddf4a9dee4a001276554cf32cf874062e1e5d7d0ebb673f6417641530bf +size 39730 From 8c29a7b55c2e27c516ba604fe5e1eefb9bbd49dc Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 29 May 2026 18:46:35 +0300 Subject: [PATCH 284/471] Merge pull request #6889 from bxdxnn/fix/read-receipt-calls Fix read receipts not shown for calls --- .../components/TimelineItemCallNotifyView.kt | 105 ++++++++++++------ .../timeline/components/TimelineItemRow.kt | 4 +- ...ts_TimelineItemCallNotifyView_Day_0_en.png | 4 +- ..._TimelineItemCallNotifyView_Night_0_en.png | 4 +- 4 files changed, 76 insertions(+), 41 deletions(-) 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 61273ff07a..fea4f92de5 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 @@ -32,7 +32,11 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState +import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView +import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent @@ -48,46 +52,63 @@ internal fun TimelineItemCallNotifyView( timelineRoomInfo: TimelineRoomInfo, event: TimelineItem.Event, content: TimelineItemRtcNotificationContent, + renderReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, onLongClick: (TimelineItem.Event) -> Unit, + onReadReceiptsClick: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier ) { - Row( - modifier = modifier - .fillMaxWidth() - .border(1.dp, ElementTheme.colors.borderInteractiveSecondary, RoundedCornerShape(8.dp)) - .combinedClickable( - enabled = true, - onClick = {}, - onLongClick = { onLongClick(event) }, - onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), + Column(modifier = modifier) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, top = 16.dp) + .border(1.dp, ElementTheme.colors.borderInteractiveSecondary, RoundedCornerShape(8.dp)) + .combinedClickable( + enabled = true, + onClick = {}, + onLongClick = { onLongClick(event) }, + onLongClickLabel = stringResource(CommonStrings.action_open_context_menu), + ) + .onKeyboardContextMenuAction { onLongClick(event) } + .padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier.size(20.sp.toDp()), + imageVector = getIcon(timelineRoomInfo, content), + contentDescription = null, + tint = ElementTheme.colors.iconSecondary, ) - .onKeyboardContextMenuAction { onLongClick(event) } - .padding(12.dp), - horizontalArrangement = Arrangement.spacedBy(12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - modifier = Modifier.size(20.sp.toDp()), - imageVector = getIcon(timelineRoomInfo, content), - contentDescription = null, - tint = ElementTheme.colors.iconSecondary, - ) - Text( - modifier = Modifier.weight(1f), - text = stringResource(getTextRes(timelineRoomInfo, content)), - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) + Text( + modifier = Modifier.weight(1f), + text = stringResource(getTextRes(timelineRoomInfo, content)), + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) - Text( - text = event.sentTime, - style = ElementTheme.typography.fontBodyMdRegular, - color = ElementTheme.colors.textSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, + Text( + text = event.sentTime, + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + isLastOutgoingMessage = isLastOutgoingMessage, + receipts = event.readReceiptState.receipts, + ), + renderReadReceipts = renderReadReceipts, + onReadReceiptsClick = { onReadReceiptsClick(event) }, + modifier = Modifier.padding(top = 4.dp), ) } } @@ -125,7 +146,10 @@ private fun getIcon( @PreviewsDayNight @Composable internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { - Column(modifier = Modifier.padding(2.dp), verticalArrangement = Arrangement.spacedBy(2.dp)) { + val readReceiptState = aTimelineItemReadReceipts( + receipts = List(3) { aReadReceiptData(it) }, + ) + Column(modifier = Modifier.padding(bottom = 16.dp)) { listOf(false, true).forEach { isDm -> listOf(CallIntent.AUDIO, CallIntent.VIDEO).forEach { callIntent -> listOf( @@ -136,9 +160,18 @@ internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { val content = TimelineItemRtcNotificationContent(callIntent, state) TimelineItemCallNotifyView( timelineRoomInfo = aTimelineRoomInfo(isDm = isDm), - event = aTimelineItemEvent(content = content), + event = aTimelineItemEvent( + content = content, + readReceiptState = readReceiptState, + ), content = content, + // Render read receipts for the first item only + renderReadReceipts = !isDm && + callIntent == CallIntent.AUDIO && + state == RtcNotificationState.Started, + isLastOutgoingMessage = false, onLongClick = {}, + onReadReceiptsClick = {}, ) } } 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 2b72be722d..5dc69bba8b 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 @@ -124,11 +124,13 @@ internal fun TimelineItemRow( } is TimelineItemRtcNotificationContent -> { TimelineItemCallNotifyView( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp), timelineRoomInfo = timelineRoomInfo, event = timelineItem, content = timelineItem.content, + renderReadReceipts = renderReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, onLongClick = onLongClick, + onReadReceiptsClick = onReadReceiptClick, ) } else -> { 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 e89697a8cc..510521faa5 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:5529e89e00208e38522f5206f5b8d304bd472b27071cab4e0d3c2daf3cb64db0 -size 49741 +oid sha256:aeaeef2348e2844e300c83b257027468ff51977bcfa2d6c540f8c882d2624826 +size 44173 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 37037907c8..5dcea94a33 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:fa29aaa82f21912dd5147ffb6fdc457fd5880e8be4abde4f0177d0ab1a412fe2 -size 48245 +oid sha256:4a6c9b24bfc9159fc70b6431f106836031c69cf5308674c20d88c1570f15d105 +size 43144 From d6892dd0907e479a5e40c46dab451a6a55a950c4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 29 May 2026 18:21:18 +0200 Subject: [PATCH 285/471] change: replace the maplibre-compose UserLocationState to a simpler one --- .../location/impl/common/UserLocationState.kt | 43 +++++++++++++++++++ .../common/ui/SimpleLocationTrackingEffect.kt | 3 +- .../impl/common/ui/UserLocationPuck.kt | 26 +---------- .../location/impl/share/ShareLocationView.kt | 4 +- .../location/impl/show/ShowLocationView.kt | 2 +- 5 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt new file mode 100644 index 0000000000..632ddef38d --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common + +import android.annotation.SuppressLint +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalInspectionMode +import org.maplibre.compose.location.DesiredAccuracy +import org.maplibre.compose.location.Location +import org.maplibre.compose.location.rememberAndroidLocationProvider +import org.maplibre.compose.location.rememberNullLocationProvider +import org.maplibre.spatialk.units.extensions.meters +import kotlin.time.Duration.Companion.seconds + +class UserLocationState(locationState: State) { + val location: Location? by locationState +} + +@SuppressLint("MissingPermission") +@Composable +fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState { + val isPreview = LocalInspectionMode.current + val locationProvider = if (isPreview || !hasLocationPermission) { + rememberNullLocationProvider() + } else { + rememberAndroidLocationProvider( + updateInterval = 5.seconds, + desiredAccuracy = DesiredAccuracy.High, + minDistance = 5.meters, + ) + } + val locationState = locationProvider.location.collectAsState() + return remember { UserLocationState(locationState) } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt index 7a7a1551cf..cda360be9e 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt @@ -12,9 +12,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow +import io.element.android.features.location.impl.common.UserLocationState import kotlinx.coroutines.flow.distinctUntilChanged import org.maplibre.compose.location.Location -import org.maplibre.compose.location.UserLocationState import kotlin.math.abs /** @@ -29,7 +29,6 @@ internal fun SimpleLocationTrackingEffect( onLocationChange: suspend (Location?) -> Unit, ) { val latestOnLocationChange by rememberUpdatedState(onLocationChange) - LaunchedEffect(locationState, enabled) { if (!enabled) return@LaunchedEffect val locationStateFlow = snapshotFlow { locationState.location } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt index 7bad736911..5bcbf6a9b2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt @@ -7,23 +7,15 @@ package io.element.android.features.location.impl.common.ui -import android.annotation.SuppressLint import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.impl.common.MapDefaults +import io.element.android.features.location.impl.common.UserLocationState import org.maplibre.compose.camera.CameraState -import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.LocationPuck import org.maplibre.compose.location.LocationPuckColors import org.maplibre.compose.location.LocationPuckSizes -import org.maplibre.compose.location.UserLocationState -import org.maplibre.compose.location.rememberAndroidLocationProvider -import org.maplibre.compose.location.rememberNullLocationProvider -import org.maplibre.compose.location.rememberUserLocationState -import org.maplibre.spatialk.units.extensions.meters -import kotlin.time.Duration.Companion.seconds @Composable fun UserLocationPuck( @@ -65,19 +57,3 @@ fun UserLocationPuck( ) } } - -@SuppressLint("MissingPermission") -@Composable -fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState { - val isPreview = LocalInspectionMode.current - val locationProvider = if (isPreview || !hasLocationPermission) { - rememberNullLocationProvider() - } else { - rememberAndroidLocationProvider( - updateInterval = 5.seconds, - desiredAccuracy = DesiredAccuracy.High, - minDistance = 5.meters, - ) - } - return rememberUserLocationState(locationProvider) -} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index 68dcee36ac..da7410f0bb 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -39,11 +39,12 @@ import io.element.android.features.location.api.Location import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.impl.R import io.element.android.features.location.impl.common.MapDefaults +import io.element.android.features.location.impl.common.UserLocationState +import io.element.android.features.location.impl.common.rememberUserLocationState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialog import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold import io.element.android.features.location.impl.common.ui.UserLocationPuck -import io.element.android.features.location.impl.common.ui.rememberUserLocationState import io.element.android.features.location.impl.share.ShareLocationEvent.StartLiveLocationShare import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.LocationPin @@ -69,7 +70,6 @@ import kotlinx.collections.immutable.ImmutableList import org.maplibre.compose.camera.CameraMoveReason import org.maplibre.compose.camera.CameraState import org.maplibre.compose.camera.rememberCameraState -import org.maplibre.compose.location.UserLocationState import kotlin.time.Duration @OptIn(ExperimentalMaterial3Api::class) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 5d5295871c..ba60a22225 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -37,13 +37,13 @@ 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.location.impl.common.MapDefaults +import io.element.android.features.location.impl.common.rememberUserLocationState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialog import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton import io.element.android.features.location.impl.common.ui.LocationPinMarkers import io.element.android.features.location.impl.common.ui.LocationShareRow import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold import io.element.android.features.location.impl.common.ui.UserLocationPuck -import io.element.android.features.location.impl.common.ui.rememberUserLocationState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight From 4c1a167e2b96d0b19fe4adc9cabc1a3476df9609 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Fri, 29 May 2026 18:18:12 +0000 Subject: [PATCH 286/471] Add HS capability check --- .../slashcommands/impl/DefaultSlashCommandService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt index 6cd8688cad..8234116bb1 100644 --- a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt +++ b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt @@ -99,6 +99,14 @@ class DefaultSlashCommandService( override suspend fun proceedAdmin( slashCommand: SlashCommand.SlashCommandAdmin, ): Result { + if (slashCommand is SlashCommand.ChangeDisplayNameForRoom) { + val canUserChangeDisplayName = withTimeoutOrNull(5.seconds) { + capabilitiesProvider.canChangeDisplayName().getOrNull() + } ?: false + if (!canUserChangeDisplayName) { + return Result.failure(Exception("Changing display name is not allowed")) + } + } return commandExecutor.proceedAdmin( slashCommand = slashCommand, ) From 81ed41d580fa85e0b3971c8f9106c619edfc65a2 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 1 Jun 2026 09:52:14 +0200 Subject: [PATCH 287/471] Sync Strings (#6921) Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com> --- .../src/main/res/values-zh/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 7 + .../src/main/res/values-fi/translations.xml | 4 + .../src/main/res/values-fr/translations.xml | 4 + .../src/main/res/values-ja/translations.xml | 5 + .../src/main/res/values-pl/translations.xml | 7 + .../src/main/res/values-ru/translations.xml | 8 + .../src/main/res/values-zh/translations.xml | 5 + .../src/main/res/values-et/translations.xml | 43 + .../src/main/res/values-pl/translations.xml | 9 + .../src/main/res/values-ru/translations.xml | 33 + .../src/main/res/values-zh/translations.xml | 13 +- .../src/main/res/values-et/translations.xml | 2 + .../src/main/res/values-zh/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 3 + .../src/main/res/values-pl/translations.xml | 3 +- .../src/main/res/values-ru/translations.xml | 7 + .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values/localazy.xml | 1 + ....impl.share_ShareLocationView_Day_9_de.png | 3 + ...on.impl.show_ShowLocationView_Day_8_de.png | 3 + ...eeditor_AttachmentImageEditorView_0_de.png | 3 + ...eeditor_AttachmentImageEditorView_1_de.png | 3 + ...eeditor_AttachmentImageEditorView_2_de.png | 3 + ...eeditor_AttachmentImageEditorView_3_de.png | 3 + ...eeditor_AttachmentImageEditorView_4_de.png | 3 + ...eeditor_AttachmentImageEditorView_5_de.png | 3 + ...ts.preview_AttachmentsPreviewView_0_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_1_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_2_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_3_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_4_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_5_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_6_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_7_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_8_de.png | 4 +- ...ts.preview_AttachmentsPreviewView_9_de.png | 3 + ...ts_TimelineItemCallNotifyView_Day_0_de.png | 4 +- ...ions_NotificationSettingsView_Day_0_de.png | 4 +- ...ons_NotificationSettingsView_Day_14_de.png | 3 + ...ons_NotificationSettingsView_Day_15_de.png | 3 + ...ons_NotificationSettingsView_Day_16_de.png | 3 + ...ons_NotificationSettingsView_Day_17_de.png | 3 + ...ons_NotificationSettingsView_Day_18_de.png | 3 + ...ons_NotificationSettingsView_Day_19_de.png | 3 + ...ions_NotificationSettingsView_Day_1_de.png | 4 +- ...ons_NotificationSettingsView_Day_20_de.png | 3 + ...ions_NotificationSettingsView_Day_2_de.png | 4 +- ...ions_NotificationSettingsView_Day_3_de.png | 4 +- ...ions_NotificationSettingsView_Day_4_de.png | 4 +- ...ions_NotificationSettingsView_Day_5_de.png | 4 +- ...ions_NotificationSettingsView_Day_6_de.png | 4 +- ...ions_NotificationSettingsView_Day_7_de.png | 4 +- ...ions_NotificationSettingsView_Day_8_de.png | 4 +- ...ions_NotificationSettingsView_Day_9_de.png | 4 +- ...e.impl.bugreport_BugReportViewDay_0_de.png | 4 +- ...e.impl.bugreport_BugReportViewDay_1_de.png | 4 +- ...e.impl.bugreport_BugReportViewDay_2_de.png | 4 +- ...e.impl.bugreport_BugReportViewDay_3_de.png | 4 +- ...e.impl.bugreport_BugReportViewDay_4_de.png | 4 +- screenshots/html/data.js | 2150 +++++++++-------- 61 files changed, 1337 insertions(+), 1122 deletions(-) create mode 100644 screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png create mode 100644 screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png create mode 100644 screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png diff --git a/features/home/impl/src/main/res/values-zh/translations.xml b/features/home/impl/src/main/res/values-zh/translations.xml index 39900a6f0b..575adc6bb9 100644 --- a/features/home/impl/src/main/res/values-zh/translations.xml +++ b/features/home/impl/src/main/res/values-zh/translations.xml @@ -4,7 +4,7 @@ "禁用优化" "通知未送达?" "通知提示音已升级:更清晰、更快速、干扰更少。" - "我们已更新你的声音" + "我们已更新提示音" "你的聊天已被端到端加密自动备份。如果你无法访问所有设备,则需要使用恢复密钥并保留数字身份。" "获取恢复密钥" "备份聊天" diff --git a/features/messages/impl/src/main/res/values-et/translations.xml b/features/messages/impl/src/main/res/values-et/translations.xml index 506b5ce195..aa5a9045d1 100644 --- a/features/messages/impl/src/main/res/values-et/translations.xml +++ b/features/messages/impl/src/main/res/values-et/translations.xml @@ -16,6 +16,12 @@ "Reisimine ja kohad" "Hiljutised emojid" "Sümbolid" + "Pööra pilti vasakule" + + "%1$d kraad" + "%1$d kraadi" + + "Muuda fotot" "Selgitused ja alapealkirjad ei pruugi olla nähtavad vanemate rakenduste kasutajatele." "Klõpsa üleslaaditava video kvaliteedi muutmiseks" "Faili üleslaadimine ei õnnestunud." @@ -26,6 +32,7 @@ "Objekt %1$d/%2$d" "Optimeeri pildikvaliteeti" "Töötlen…" + "Lisa meediumi" "Blokeeri kasutaja" "Vali see eelistus, kui sa soovid peita selle kasutaja kõik senised ja tulevased sõnumid" "Teade selle sõnumi kohta edastatakse sinu koduserveri haldajale. Haldajal ei ole võimalik lugeda krüptitud sõnumite sisu." diff --git a/features/messages/impl/src/main/res/values-fi/translations.xml b/features/messages/impl/src/main/res/values-fi/translations.xml index 33e8f64d57..25f434ec25 100644 --- a/features/messages/impl/src/main/res/values-fi/translations.xml +++ b/features/messages/impl/src/main/res/values-fi/translations.xml @@ -16,6 +16,10 @@ "Matkustaminen ja paikat" "Viimeaikaiset emojit" "Symbolit" + + "%1$d aste" + "%1$d astetta" + "Kuvatekstit eivät välttämättä näy ihmisille, jotka käyttävät vanhempia sovelluksia." "Napauta muuttaaksesi videon lähetyslaatua" "Tiedostoa ei voitu lähettää." diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 0d8dab284f..9137d5dae6 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -16,6 +16,10 @@ "Voyages & lieux" "Emojis récents" "Symboles" + + "%1$d degré" + "%1$d degrés" + "Les légendes peuvent ne pas être visibles pour les utilisateurs d’anciennes applications." "Cliquez pour modifier la qualité d’envoi de la vidéo" "Le fichier n’a pas pu être envoyé." diff --git a/features/messages/impl/src/main/res/values-ja/translations.xml b/features/messages/impl/src/main/res/values-ja/translations.xml index 93c10d13d8..f1a4bde02f 100644 --- a/features/messages/impl/src/main/res/values-ja/translations.xml +++ b/features/messages/impl/src/main/res/values-ja/translations.xml @@ -16,6 +16,11 @@ "旅・場所" "最近使用" "記号" + "画像を左に回転" + + "%1$d°" + + "写真を編集" "古いアプリケーションを使用しているユーザーはキャプションを見られない可能性があります。" "動画のアップロード画質を変更するにはタップしてください" "ファイルをアップロードに失敗しました。" diff --git a/features/messages/impl/src/main/res/values-pl/translations.xml b/features/messages/impl/src/main/res/values-pl/translations.xml index a0b9c24af2..1a9dc12f1c 100644 --- a/features/messages/impl/src/main/res/values-pl/translations.xml +++ b/features/messages/impl/src/main/res/values-pl/translations.xml @@ -16,6 +16,13 @@ "Podróż i miejsca" "Ostatnie emoji" "Symbole" + "Obróć obraz w lewo" + + "%1$d stopień" + "%1$d stopnie" + "%1$d stopni" + + "Edytuj zdjęcie" "Opis może być niedostępny dla osób korzystających ze starszej wersji aplikacji." "Dotknij, aby zmienić jakość przesyłania wideo." "Nie udało się przesłać pliku." diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index a7d22b6542..5c3aa6f5e1 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -16,6 +16,13 @@ "Путешествия и места" "Недавние эмодзи" "Символы" + "Повернуть изображение влево." + + "%1$d градус" + "%1$d градуса" + "%1$d градусов" + + "Редактировать фото" "Подпись может быть не видна пользователям старых приложений." "Нажмите, чтобы изменить качество загружаемого видео." "Файл не может быть загружен." @@ -26,6 +33,7 @@ "%1$d из %2$d" "Оптимизировать качество изображения" "Обработка…" + "Добавить медиа" "Заблокировать пользователя" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." diff --git a/features/messages/impl/src/main/res/values-zh/translations.xml b/features/messages/impl/src/main/res/values-zh/translations.xml index bdd898d26f..c572b737f1 100644 --- a/features/messages/impl/src/main/res/values-zh/translations.xml +++ b/features/messages/impl/src/main/res/values-zh/translations.xml @@ -16,6 +16,11 @@ "文旅景点" "最近的 Emoji" "符号" + "向左旋转图像" + + "%1$d 度" + + "编辑照片" "使用旧版应用程序的用户可能无法看到字幕。" "点按以更改视频上传质量" "无法上传该文件。" 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 d1e2b91d58..fab8eeff41 100644 --- a/features/preferences/impl/src/main/res/values-et/translations.xml +++ b/features/preferences/impl/src/main/res/values-et/translations.xml @@ -58,6 +58,7 @@ "Kas tahad katsetada?" "Katsed" "Täiendavad seadistused" + "Kõne helin" "Hääl- ja videokõned" "Eelistused ei sobi omavahel" "Et eelistusi oleks kergem leida, me oleme lihtsustanud teavituste seadistusi. Kuigi mõned varem valitud eelistused pole siin näha, siis nad kehtivad jätkuvalt. @@ -76,10 +77,52 @@ Kui sa jätkad muutmist, siis võivad muutuda ka need peidetud eelistused.""Kutsed" "Sinu koduserver ei toeta seda funktsionaalsust krüptitud jututubades ja seega ei pruugi kõik teavitused sinuni jõuda." "Mainimiste alusel" + "Vali muu ​​helin…" + "Hetkel on kasutusel %1$s" + "Sõnumi heli" + "Sõnumi heli" "Kõik" "Mainimiste alusel" "Teavita mind" "Teavita mind @jututoa puhul" + "Kohandatud" + "Sinu valitud helin…" + "Viga faili kustutamisel" + "Elementi vaikimisi väärtus" + "Elemendi hajumine" + "Viga faili importimisel" + "Viga helina eelvaate esitamisel" + "Helin" + "Sulge helinavea viga" + "Viga helina seadistamisel" + "Vaikne" + "Hoiatus" + "Ootus" + "Kelluke" + "Õitsemine" + "Kalüpso" + "Kellamäng" + "Tšuhh-tšuhh" + "Süsteemi vaikimisi väärtus" + "Laskumine" + "Elektrooniline" + "Tuututamine" + "Klaas" + "Pasun" + "Redel" + "Menuett" + "Uudisvälgatus" + "Sünge" + "Sherwoodi mets" + "Loits" + "Põnevus" + "Vihin-vuhin" + "Telegraaf" + "Kikivarvul" + "Kolmetooniline" + "Säuts" + "Kirjutusmasinad" + "Uuenda" "Teavituste saamiseks palun muuda oma %1$s." "süsteemi seadistusi" "Süsteemi teavitused on välja lülitatud" 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 ca81984032..44bb62acfe 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -59,6 +59,7 @@ "Chcesz poeksperymentować?" "Laboratoria" "Dodatkowe ustawienia" + "Dzwonek połączenia" "Połączenia audio i wideo" "Niezgodność konfiguracji" "Uprościliśmy Ustawienia powiadomień, aby ułatwić nawigowanie między opcjami. Niektóre ustawienia, które wybrałeś mogły zniknąć, lecz są wciąż aktywne. @@ -77,10 +78,15 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Zaproszenia" "Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi." "Wzmianki" + "Wybierz inny dźwięk…" + "Aktualnie używany %1$s" + "Dźwięk wiadomości" + "Dźwięk wiadomości" "Wszystkie" "Wzmianki" "Powiadamiaj mnie przez" "Powiadom mnie na @pokój" + "Własny" "Własny dźwięk…" "Błąd usuwania pliku" "Domyślny Element" @@ -88,7 +94,9 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Błąd importowania pliku" "Wystąpił błąd podczas podglądania dźwięku powiadomień" "Dźwięk" + "Pomiń dźwięk błędu" "Nie udało się ustawić dźwięku powiadomień" + "Cichy" "Alert" "Oczekiwanie" "Dzwonek" @@ -96,6 +104,7 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Kalipso" "Gong" "Ciuchcia" + "Ustawienie domyślne" "Opadający" "Elektroniczna" "Fanfary" 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 eb4ad2245d..1987a17643 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -81,6 +81,39 @@ "Упоминания" "Уведомлять меня" "Уведомлять меня при упоминании @room" + "Пользовательский звук…" + "Ошибка при удалении файла" + "Element по умолчанию" + "Ошибка импорта файла" + "Ошибка предварительного прослушиванием звука" + "Звук" + "Проблема с настройкой звукового оповещения" + "Оповещение" + "Anticipate" + "Bell" + "Bloom" + "Calypso" + "Chime" + "Choo Choo" + "Descent" + "Electronic" + "Fanfare" + "Glass" + "Horn" + "Ladder" + "Minuet" + "News Flash" + "Noir" + "Sherwood Forest" + "Spell" + "Suspense" + "Swish" + "Telegraph" + "Tiptoes" + "Tri-tone" + "Tweet" + "Typewriters" + "Обновить" "Чтобы получать уведомления, измените %1$s." "системные настройки" "Системные уведомления выключены" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index 3726b82206..b5d9d702c9 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -57,6 +57,7 @@ "想尝试新功能?" "实验室" "更多设置" + "来电铃声" "音视频通话" "配置不匹配" "我们简化了通知设置,使选项更易于查找。你曾经选择的某些自定义设置未在此处显示,但它们仍然有效。 @@ -75,18 +76,25 @@ "邀请" "主服务器不支持在加密房间中的此选项,因此在某些房间你可能无法收到通知。" "提及" + "选择其它提示音…" + "当前正在使用 %1$s" + "消息提示音" + "消息提示音" "全部" "提及" "通知我以下类型" "提及所有成员(@room)时通知我" - "自定义声音…" + "自定义" + "自定义提示音…" "删除文件时出错" "Element 默认" "Element 淡入" "导入文件时出错" "预览提示音时出现问题" - "声音" + "提示音" + "忽略提示音错误" "设置提示音时出现问题" + "静默" "提醒声" "悉心期盼" "铃铛" @@ -94,6 +102,7 @@ "即兴曲调" "风铃" "火车鸣笛" + "系统默认" "渐弱" "电子乐" "嘹亮吹奏声" diff --git a/features/rageshake/impl/src/main/res/values-et/translations.xml b/features/rageshake/impl/src/main/res/values-et/translations.xml index 2f55a9b6b5..1f61849fa5 100644 --- a/features/rageshake/impl/src/main/res/values-et/translations.xml +++ b/features/rageshake/impl/src/main/res/values-et/translations.xml @@ -8,6 +8,8 @@ "Palun kirjelda probleemi…" "Kui vähegi võimalik, siis kirjuta inglise keeles." "Kirjeldus on liiga lühike. Palun jaga täpsemat teavet selle kohta, mis juhtus. Tänud juba ette!" + "Võid sisestada seotud GitHubi veateate numbri, kui see on olemas." + "Veateade GitHubis" "Saada krahhilogid" "Luba logide saatmine" "Sinu logid on väga mahukad ja neid ei saa siia lisada. Palun saada logid meile mõnel muul viisil." diff --git a/features/rageshake/impl/src/main/res/values-zh/translations.xml b/features/rageshake/impl/src/main/res/values-zh/translations.xml index c42141d291..17cac7de3b 100644 --- a/features/rageshake/impl/src/main/res/values-zh/translations.xml +++ b/features/rageshake/impl/src/main/res/values-zh/translations.xml @@ -8,6 +8,7 @@ "描述问题…" "请尽可能用英文描述。" "描述太短,请提供详细情况。谢谢!" + "你可以输入与之相关的 GitHub issue 编号(如果有)。" "发送崩溃日志" "允许日志" "日志文件过大,无法包含在本次报告中,请通过其它方式发送给我们。" 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 f1c7d8d225..fd45849e7d 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -18,6 +18,7 @@ "Teave" "Liitu kõnega" "Mine lõppu" + "Mine lugemata sõnumite juurde" "Nihuta kaart minu asukohta" "Ainult mainimised" "Summutatud" @@ -98,6 +99,7 @@ "Keeldu ja blokeeri" "Kustuta" "Kustuta kasutajakonto" + "Kustuta fail" "Kustuta küsitlus" "Eemalda kõik valikud" "Lülita välja" @@ -303,6 +305,7 @@ Põhjus: %1$s." "Palun oota…" "Kas oled kindel, et soovid selle küsitluse lõpetada?" "Küsitlus: %1$s" + "Küsitlus" "Hääli kokku: %1$s" "Tulemused on näha peale küsitluse lõppemist" 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 7e0b78641e..baa2eb8506 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -308,6 +308,7 @@ Powód: %1$s." "Proszę czekać…" "Jesteś pewien, że chcesz zakończyć tę ankietę?" "Ankieta: %1$s" + "Ankieta" "Łączna liczba głosów: %1$s" "Wyniki zostaną wyświetlone po zakończeniu ankiety" @@ -424,7 +425,7 @@ Powód: %1$s." "Balans między jakością a szybkością przesyłania" "Wiadomość głosowa" "Oczekiwanie…" - "Oczekiwanie na tę wiadomość" + "Oczekiwanie wiadomości" "Czekam na lokalizację na żywo…" "Każdy może zobaczyć historię" "Ty" 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 3596bc04fe..b35d346484 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -10,6 +10,7 @@ "Введено %1$d цифры" "Введено %1$d цифр" + "Продолжительность: %1$s" "Изменить аватар" "Полный адрес %1$s" "Сведения о шифровании" @@ -18,6 +19,7 @@ "Информация" "Присоединиться к звонку" "Перейти вниз" + "Перейти к непрочитанным" "Переместить карту к моему местоположению" "Только упоминания" "Без звука" @@ -34,6 +36,7 @@ "Скорость воспроизведения" "Опрос" "Завершённый опрос" + "Позиция: %1$s" "QR-код" "Реагировать вместе с %1$s" "Реакция с помощью эмодзи" @@ -50,12 +53,15 @@ "Аватар комнаты" "Отправить файлы" "Местоположение отправителя" + "Отправлено пользователем %1$s в %2$s" "Требуется действие, на которое есть ограничение по времени, у вас есть одна минута для проверки" "Настройки, требуется действие" "Показать пароль" "Начать звонок" "Начать видеозвонок" "Начать голосовой вызов" + "Обсуждение в %1$s" + "Обсуждения в %1$s" "Брошенная комната" "Аватар пользователя" "Меню пользователя" @@ -302,6 +308,7 @@ "Подождите…" "Вы действительно хотите завершить данный опрос?" "Опрос: %1$s" + "Опрос" "Всего голосов: %1$s" "Результаты будут показаны после завершения опроса" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index e40d369f76..2acc9df99f 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -96,6 +96,7 @@ "拒绝并屏蔽" "删除" "删除账户" + "删除文件" "删除投票" "取消全选" "禁用" @@ -218,7 +219,7 @@ "正在创建房间…" "正在创建空间…" "申请已取消" - "离开房间" + "已离开房间" "离开空间" "邀请已拒绝" "解密错误" @@ -299,6 +300,7 @@ "请稍候…" "确定要结束这个投票吗?" "投票:%1$s" + "投票" "总票数:%1$s" "结果将在投票结束后显示" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index d74f532484..823c0fad72 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -99,6 +99,7 @@ "Decline and block" "Delete" "Delete account" + "Delete file" "Delete Poll" "Deselect all" "Disable" diff --git a/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png b/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png new file mode 100644 index 0000000000..d8b464213f --- /dev/null +++ b/screenshots/de/features.location.impl.share_ShareLocationView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1ab182dfc2b45c80972722b1963a75c29175c1d09369fafb96c7b0ea04bf08a +size 21171 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png new file mode 100644 index 0000000000..a49023fb61 --- /dev/null +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa435ee5d684b82c66c7207e0bcdc2d608a634e02d534530cccf8abd0f40f784 +size 21292 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png new file mode 100644 index 0000000000..6c398bfae2 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56f7a86707c00d1833ebbb26182ad9f9f010ed8f016713076232d4134c8bb6e7 +size 329737 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png new file mode 100644 index 0000000000..25ae9b6949 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6907cdde67cfb41246b9b86af19d0d46bf92914b21f9ab68d522f07dd83af153 +size 284058 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png new file mode 100644 index 0000000000..0d11558b0e --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ec83baa6376355fcfa1e9a088faee47f5d7d87ed7608bcbe35e7438f9e0cdea +size 257324 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png new file mode 100644 index 0000000000..6fadc05d09 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17bd7ae449ad1fb92c75e09848bddf907d15cfcbbcb9554350893c7a6019add5 +size 278513 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png new file mode 100644 index 0000000000..3eba67d146 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e692a9e204db51af62fa83af8c1c7dda4fdfe81077d3abced093f3f8dfdef2df +size 252607 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png new file mode 100644 index 0000000000..cb1f4a9175 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e881c0e26edfd9f4f2eb7325e307edcd4220aae42337ce8d238cd64c88722de4 +size 312002 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_de.png index 3d408f0ee2..c9ec0e5253 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c110f007c954fd01fad8d4e9810abae47bb89c330141c7c1d6412d3995943f4b -size 400985 +oid sha256:14a4be90563169ef442341a03c8add67df6ad2385b23221f7a484b296d47cab9 +size 405385 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_de.png index 4ce254bb81..a1fdd29d48 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af9841032d9faebbee6f759259ef14b0d4cca3424ac110e1dfd1d3357768e436 -size 399354 +oid sha256:c15b4ac06b3327c95f4e40556e2c7cc0fdd0de6942dcc41a06545b03f3d7efb9 +size 403700 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_de.png index d466ee2bc4..7700d70b06 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fae17aad6ac7dfb2fd2fac83c2c1b6d9e0743fa9d3d1a874fe269284149ce29 -size 62760 +oid sha256:608236a6a1864f0036fb0d2bcd85d7269557dc3453876495585573471d08d2de +size 65085 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_de.png index 3d408f0ee2..c9ec0e5253 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c110f007c954fd01fad8d4e9810abae47bb89c330141c7c1d6412d3995943f4b -size 400985 +oid sha256:14a4be90563169ef442341a03c8add67df6ad2385b23221f7a484b296d47cab9 +size 405385 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_de.png index c45996cc7b..c9b90482e3 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbcda50596ea08167fda422fae89758bc46c719cd320103def3c90cea0a152ff -size 62616 +oid sha256:116502f69435e6d7a5fe9fe9a2a604fcca36d1c6e1e09836595769cf8e8022d9 +size 64937 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_de.png index a1ae4a1c9b..c10cca29ea 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b1172f37d89f97ec0360402c013328ad65bfb62422d5b81a8d06e9d8dc3cbba -size 67739 +oid sha256:eb69e3738018bd6138e1f04478b5f7b5758816b9516f871dc8643ea037ba1c65 +size 71377 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png index 3baea3a223..11e4bb1119 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29d6a3980b5928907e5d5203ba4a5010feed5ca67107ef464933717664d72fb1 -size 71937 +oid sha256:130c46b4bd3589cc6a6e45a04f9793b742631daf21cbbf971f8e79a00f589ce8 +size 329628 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_de.png index be72870cdf..d68e7eb35d 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df744636336e3b7dcf4b8c20a3a9dcf70247ea0a5602c11665bb020925c94dd1 -size 407934 +oid sha256:6bea145d686c295829e91a0a19e4172155ddd03ae62730241ee55e3c759d5f88 +size 75505 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_de.png index 48b54318d5..b40455736b 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea59b97a710eab6b10192373c7058b301e0d2b4bfe07d16f952d69593b8b78d7 -size 88581 +oid sha256:7587dfe56eb8de6c4ed8117ad9bf1066cd53b5e89430be66a9b9f3ae357343fe +size 412192 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png new file mode 100644 index 0000000000..dee57573b1 --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a1a324578ba710175ade8d54b2f99e8103b893d7a3e7d1353ddc2b2c0c3a285 +size 91939 diff --git a/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png index d005896ab7..b6f95124eb 100644 --- a/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08984e24e297742f065201c4cea3d07eda34e4c80fbcb4689e539ac64f32f1db -size 53795 +oid sha256:a2d68bc8db6190fbd5205fdaa386c250ae3a2014804a5372052d851a313a6ef1 +size 47808 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png index 446b8185f5..3506d44218 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26f2620aa348febb2db9ebe379a964b76220819f7165fa7a5be5e2a38c2534a2 -size 67183 +oid sha256:cdde4c4250683a2cf63988a92dc03cbc64581794c9f25567ecca61675edea9dc +size 64428 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png new file mode 100644 index 0000000000..4b59292eaa --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_14_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:168c2399ac6262c65e1026e3c79fe2c93dd6805ac73b9bc26adfaad8afeab724 +size 53258 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png new file mode 100644 index 0000000000..de235a8eb6 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_15_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:950b6b93b0b31d0bef28e6b7ff0759e1e6e3ee48fac8278035665247f1176e8b +size 51887 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png new file mode 100644 index 0000000000..d46754d4f0 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_16_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d53770762a7b2477787fb21acb17303b671d549b4eab5f54ccbf6d1c566aa50 +size 53023 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png new file mode 100644 index 0000000000..06c3f12377 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_17_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32abcd8e957508558dde739c881f2ce9efac648404c420aac44127d0b449d5c4 +size 53000 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png new file mode 100644 index 0000000000..6e9281a6b2 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_18_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6bf8bb2491b685153fb11542be0de2a95cdd00ab294cf0bf882e890f8f5f697 +size 48682 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png new file mode 100644 index 0000000000..7439ba1f73 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_19_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4f67eb6df70b214682b9b337886cee1653c3804be2b3a6ed581247c22fc0d5f +size 44778 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png index e0fac995a2..06c3f12377 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9ebab37f3d289cd684d616961b2f4b993a851a34bb8d41ce889443ce51c4c5f -size 53981 +oid sha256:32abcd8e957508558dde739c881f2ce9efac648404c420aac44127d0b449d5c4 +size 53000 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png new file mode 100644 index 0000000000..10bda48dc6 --- /dev/null +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_20_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:058a2ef322426a6c996698c5c0df64f8d4cec5107b918101478a6f478897c3c1 +size 46365 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png index 286ee2d3e3..5cd18f7bfa 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d578725c9ef0c37be7115017fe1b7f7da41c02a54f932ee389d4683f2ec3f524 -size 48465 +oid sha256:78109755f9bb674e0b026f8696dcc5024e1aa07838d404954e17edcf8d752e80 +size 47441 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png index 8428aafe61..252f3c7397 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e949b9fc728afe5da33073a5273aebe797a7c45a68fc6049e9292d0a256adad7 -size 49632 +oid sha256:288f3c3887439cfe8b4fea6ba7b5091c2342cf9e962ad2ddbbeea99243feddaf +size 48577 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png index 8428aafe61..252f3c7397 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e949b9fc728afe5da33073a5273aebe797a7c45a68fc6049e9292d0a256adad7 -size 49632 +oid sha256:288f3c3887439cfe8b4fea6ba7b5091c2342cf9e962ad2ddbbeea99243feddaf +size 48577 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png index e0fac995a2..06c3f12377 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9ebab37f3d289cd684d616961b2f4b993a851a34bb8d41ce889443ce51c4c5f -size 53981 +oid sha256:32abcd8e957508558dde739c881f2ce9efac648404c420aac44127d0b449d5c4 +size 53000 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png index b107216acc..dbc959895b 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23548dc4c9b3ede93580ce56989ff05d4317605aaa584744f73a5897d0cd6c51 -size 46743 +oid sha256:56fbcdd514a6fc964e0713ee18490f801bee4b5b91f1ebbf9471262af37830e1 +size 45656 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png index 058db215b6..9d81cecd3f 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a284cc8503c3cd6227afbd593d36e6f9a9eeadbd323b5cf66d77e3b5eeb6681 -size 51713 +oid sha256:a2bc82e2fa80c56ef65b462421c4b0204e6077547226b799c5a9a47367a1dfb2 +size 50724 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png index e0fac995a2..06c3f12377 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9ebab37f3d289cd684d616961b2f4b993a851a34bb8d41ce889443ce51c4c5f -size 53981 +oid sha256:32abcd8e957508558dde739c881f2ce9efac648404c420aac44127d0b449d5c4 +size 53000 diff --git a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png index e0fac995a2..06c3f12377 100644 --- a/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png +++ b/screenshots/de/features.preferences.impl.notifications_NotificationSettingsView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9ebab37f3d289cd684d616961b2f4b993a851a34bb8d41ce889443ce51c4c5f -size 53981 +oid sha256:32abcd8e957508558dde739c881f2ce9efac648404c420aac44127d0b449d5c4 +size 53000 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png index 42885de40c..429f3b3bf0 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f935cbeb516a345549877793ed1e0e5d82b0b1b81b65d54f028d8ee30a3eeb7c -size 50075 +oid sha256:b3309c34f8127f41b85f915246d87b257befd909c6ea13151f018f2af838cee3 +size 55553 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png index afb28a7513..3386f18dd6 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54c0d5db8026616d766d1ceb94fbac1c3588be1ac3baa38e05ef4f665aa9419f -size 115721 +oid sha256:4d11dbfc86f54a219d989672e5a5f2c729e1373706be22256aa913d3a2f67eb8 +size 116355 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png index 4b03d29ff9..46a240da00 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8efca7b9be49bd84f809fac58de983e6126714914250ce4b0dcb96a56125bb7 -size 46915 +oid sha256:2a786030fface63e75009179d2473c5ec880cfb194ba43a85f2bbef3090a205b +size 52774 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png index 42885de40c..429f3b3bf0 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f935cbeb516a345549877793ed1e0e5d82b0b1b81b65d54f028d8ee30a3eeb7c -size 50075 +oid sha256:b3309c34f8127f41b85f915246d87b257befd909c6ea13151f018f2af838cee3 +size 55553 diff --git a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png index 473b887d73..6b799d3180 100644 --- a/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png +++ b/screenshots/de/features.rageshake.impl.bugreport_BugReportViewDay_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3d7c97667774e00e618be65e63423d9d62c8b30dfe95cddad1d5dc62255e38c -size 39638 +oid sha256:90360b93852bbcbcf694f892075401d9019f0b28f34abbe24f3546a9db62fd81 +size 44307 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 119bc1e013..edcd08b481 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -2,99 +2,105 @@ export const screenshots = [ ["en","en-dark","de",], ["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",0,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20595,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20602,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20595,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20595,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20595,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20595,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20595,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20595,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20595,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20595,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20595,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20595,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20595,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20602,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20602,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20602,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20602,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20602,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20602,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20602,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20602,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20602,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20602,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20602,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20595,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20595,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20602,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20602,], ["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",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20602,], ["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",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20595,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20595,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20595,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20595,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20595,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20595,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20595,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20595,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20595,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20595,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20602,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20602,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20602,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20602,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20602,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20602,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20602,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20602,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20602,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20595,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20595,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20595,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20595,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20595,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20602,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20602,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20602,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20602,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20602,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20595,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20595,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20595,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20595,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20602,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20602,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20602,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20602,], ["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",20595,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20602,], ["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",20595,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20602,], ["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",20595,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20602,], ["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",20595,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20605,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20605,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20605,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20605,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20605,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20605,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20602,], ["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,], @@ -104,19 +110,20 @@ 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_AttachmentsPreviewView_0_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20595,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20595,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20602,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20605,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20595,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20602,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -146,22 +153,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20595,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20602,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20595,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20595,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20602,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20595,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20595,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20595,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20595,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20595,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20602,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20602,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20602,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20602,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20602,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -172,141 +179,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20595,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20595,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20602,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20602,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20595,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20602,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20595,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20595,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20595,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20595,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20595,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20595,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20602,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20602,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20602,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20602,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20602,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20602,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20595,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20595,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20595,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20602,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20602,], ["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",20595,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20595,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20595,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20595,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20595,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20602,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20602,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20602,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20602,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20602,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20595,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20602,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20595,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20595,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20595,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20595,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20595,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20595,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20595,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20595,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20602,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20602,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20602,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20602,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20602,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20602,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20602,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20602,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20595,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20602,], ["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",20595,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20595,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20595,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20595,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20595,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20595,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20595,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20602,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20602,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20595,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20595,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20595,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20595,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20595,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20595,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20602,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20602,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20602,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20602,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20602,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20595,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20595,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20595,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20595,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20595,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20595,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20595,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20595,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20595,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20595,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20595,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20595,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20595,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20595,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20595,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20595,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20595,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20595,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20595,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20595,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20602,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20602,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20602,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20602,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20602,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20602,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20602,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20602,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20602,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20602,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20602,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20602,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20602,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20602,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20602,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20602,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20602,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20602,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20602,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20602,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20595,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20595,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20595,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20595,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20595,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20595,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20595,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20602,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20602,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20602,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20602,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20602,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20602,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20602,], ["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",20595,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20595,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20595,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20602,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20602,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20602,], ["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",20595,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20602,], ["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",20595,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20595,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20595,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20595,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20595,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20595,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20595,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20595,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20602,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20602,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20602,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20602,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20602,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20602,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20602,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20602,], ["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,], @@ -319,20 +326,20 @@ 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",20595,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20595,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20595,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20595,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20595,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20595,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20595,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20595,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20595,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20595,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20595,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20595,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20595,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20595,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20602,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20602,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20602,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20602,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20602,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20602,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20602,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20602,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20602,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20602,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20602,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20602,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20602,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20602,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -340,29 +347,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20595,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20595,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20602,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20602,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20595,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20595,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20595,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20595,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20595,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20595,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20595,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20595,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20595,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20602,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20602,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20602,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20602,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20602,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -382,49 +389,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20595,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20595,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20595,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20602,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20602,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20602,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20595,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20595,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20602,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20602,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20595,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20602,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20595,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20595,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20595,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20595,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20595,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20602,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20602,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20602,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20602,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20602,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20595,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20595,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20602,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20602,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20595,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20595,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20602,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20602,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20595,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20595,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20595,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20595,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20595,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20595,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20595,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20595,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20595,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20595,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20595,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20595,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20595,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20602,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20602,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20602,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20602,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20602,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20602,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20602,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20602,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20602,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20602,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20602,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20602,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20602,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -439,8 +446,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_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",20595,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20595,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20602,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20602,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -448,119 +455,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20595,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20602,], ["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",20595,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20602,], ["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",20595,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20595,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20602,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20602,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20595,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20595,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20602,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20595,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20595,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20595,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20602,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20602,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20595,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20595,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20595,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20602,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20595,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20595,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20595,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20602,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20602,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20602,], ["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",20595,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20595,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20595,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20595,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20595,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20602,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20602,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20602,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20595,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20595,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20602,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20602,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20595,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20595,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20595,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20602,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20602,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20595,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20595,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20595,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20595,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20595,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20602,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20595,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20602,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -613,48 +620,48 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], -["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20595,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20602,], ["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",20595,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20602,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20595,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20595,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20595,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20602,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20602,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20602,], ["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",20595,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20595,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20595,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20595,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20595,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20595,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20595,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20595,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20595,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20595,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20595,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20595,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20595,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20595,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20595,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20595,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20595,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20595,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20595,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20595,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20595,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20595,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20595,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20595,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20595,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20595,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20595,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20602,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20602,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20602,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20602,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20602,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20602,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20602,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20602,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20602,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20602,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20602,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20602,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20602,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20602,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20602,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20602,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20602,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20602,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20602,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20602,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20602,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20602,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20602,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20602,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20602,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20602,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20602,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20595,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20595,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20595,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20595,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20602,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20602,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20602,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20602,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -667,26 +674,26 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20595,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20595,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20595,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20595,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20595,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20595,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20602,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20602,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20602,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20602,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20602,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20602,], ["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20595,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20595,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20602,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20602,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -694,10 +701,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20595,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], @@ -707,7 +714,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], @@ -717,10 +724,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20595,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], @@ -730,7 +737,7 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20595,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20602,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -744,7 +751,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",20595,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20602,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -753,7 +760,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20595,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20602,], ["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,], @@ -762,24 +769,24 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20595,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20595,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20595,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20595,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20595,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20595,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20595,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20595,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20595,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20595,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20595,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20595,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20595,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20595,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20595,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20595,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20602,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20602,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20602,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20602,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20602,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20602,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20602,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20602,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20602,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20602,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20602,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20602,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20602,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20602,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20602,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20602,], ["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",20595,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20602,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -790,117 +797,124 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20595,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20595,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20595,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20605,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20602,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20602,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20602,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20595,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20595,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20602,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20595,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20602,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20595,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20595,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20602,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20595,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20595,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20595,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20595,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20595,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20595,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20602,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20602,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20602,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20602,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20602,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20602,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20595,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20595,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20602,], ["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",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20595,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20595,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20595,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20595,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20595,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20595,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20602,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20602,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20602,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20602,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20602,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20595,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20595,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20595,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20595,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20595,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20602,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20602,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20602,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20602,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20602,], ["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",20595,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20595,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20595,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20595,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20595,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20595,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20595,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20595,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20595,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20595,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20595,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20602,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20602,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20602,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20602,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20602,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20602,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20602,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20602,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20602,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20602,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20602,], ["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,], @@ -914,225 +928,225 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20595,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20595,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20602,], ["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","",20595,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20595,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20602,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20602,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20595,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20595,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20595,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20595,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20595,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20595,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20595,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20595,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20595,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20595,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20595,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20595,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20595,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20595,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20595,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20595,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20602,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20602,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20602,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20602,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20602,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20602,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20602,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20602,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20602,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20602,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20602,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20602,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20602,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20602,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20602,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20602,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20595,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20595,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20602,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20602,], ["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",20595,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20595,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20595,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20595,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20595,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20595,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20595,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20602,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20602,], ["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",20595,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20595,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20595,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20595,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20595,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20595,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20595,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20595,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20595,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20595,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20595,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20595,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20595,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20595,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20595,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20595,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20595,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20602,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20602,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20602,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20602,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20602,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20602,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20602,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20602,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20602,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20602,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20602,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20602,], ["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",20595,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20595,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20595,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20595,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20595,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20602,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20602,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20602,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20602,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20602,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20595,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20595,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20602,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20602,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20595,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20595,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20595,], -["features.roomdetails.impl_RoomDetails_0_en","",20595,], -["features.roomdetails.impl_RoomDetails_10_en","",20595,], -["features.roomdetails.impl_RoomDetails_11_en","",20595,], -["features.roomdetails.impl_RoomDetails_12_en","",20595,], -["features.roomdetails.impl_RoomDetails_13_en","",20595,], -["features.roomdetails.impl_RoomDetails_14_en","",20595,], -["features.roomdetails.impl_RoomDetails_15_en","",20595,], -["features.roomdetails.impl_RoomDetails_16_en","",20595,], -["features.roomdetails.impl_RoomDetails_17_en","",20595,], -["features.roomdetails.impl_RoomDetails_18_en","",20595,], -["features.roomdetails.impl_RoomDetails_19_en","",20595,], -["features.roomdetails.impl_RoomDetails_1_en","",20595,], -["features.roomdetails.impl_RoomDetails_20_en","",20595,], -["features.roomdetails.impl_RoomDetails_21_en","",20595,], -["features.roomdetails.impl_RoomDetails_22_en","",20595,], -["features.roomdetails.impl_RoomDetails_2_en","",20595,], -["features.roomdetails.impl_RoomDetails_3_en","",20595,], -["features.roomdetails.impl_RoomDetails_4_en","",20595,], -["features.roomdetails.impl_RoomDetails_5_en","",20595,], -["features.roomdetails.impl_RoomDetails_6_en","",20595,], -["features.roomdetails.impl_RoomDetails_7_en","",20595,], -["features.roomdetails.impl_RoomDetails_8_en","",20595,], -["features.roomdetails.impl_RoomDetails_9_en","",20595,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20595,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20595,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20595,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20595,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20595,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20595,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20595,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20595,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20595,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20602,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20602,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20602,], +["features.roomdetails.impl_RoomDetails_0_en","",20602,], +["features.roomdetails.impl_RoomDetails_10_en","",20602,], +["features.roomdetails.impl_RoomDetails_11_en","",20602,], +["features.roomdetails.impl_RoomDetails_12_en","",20602,], +["features.roomdetails.impl_RoomDetails_13_en","",20602,], +["features.roomdetails.impl_RoomDetails_14_en","",20602,], +["features.roomdetails.impl_RoomDetails_15_en","",20602,], +["features.roomdetails.impl_RoomDetails_16_en","",20602,], +["features.roomdetails.impl_RoomDetails_17_en","",20602,], +["features.roomdetails.impl_RoomDetails_18_en","",20602,], +["features.roomdetails.impl_RoomDetails_19_en","",20602,], +["features.roomdetails.impl_RoomDetails_1_en","",20602,], +["features.roomdetails.impl_RoomDetails_20_en","",20602,], +["features.roomdetails.impl_RoomDetails_21_en","",20602,], +["features.roomdetails.impl_RoomDetails_22_en","",20602,], +["features.roomdetails.impl_RoomDetails_2_en","",20602,], +["features.roomdetails.impl_RoomDetails_3_en","",20602,], +["features.roomdetails.impl_RoomDetails_4_en","",20602,], +["features.roomdetails.impl_RoomDetails_5_en","",20602,], +["features.roomdetails.impl_RoomDetails_6_en","",20602,], +["features.roomdetails.impl_RoomDetails_7_en","",20602,], +["features.roomdetails.impl_RoomDetails_8_en","",20602,], +["features.roomdetails.impl_RoomDetails_9_en","",20602,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20602,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20602,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20602,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20602,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20602,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20602,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20602,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20602,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20602,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20595,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20595,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20595,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20595,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20595,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20595,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20595,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20595,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20595,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20595,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20595,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20602,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20602,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20602,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20602,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20602,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20602,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20602,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20602,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20602,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20602,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20602,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20595,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20595,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20595,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20602,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20602,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20602,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20595,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20595,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20602,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20595,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20595,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20595,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20595,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20595,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20595,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20602,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1155,16 +1169,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20595,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20602,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20595,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20602,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1174,118 +1188,118 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20595,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20595,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20595,], -["appicon.element_RoundIcon_en","",0,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20602,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20602,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20602,], ["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",20595,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20595,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20595,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20595,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20595,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20595,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20595,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20602,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20602,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20602,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20602,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20602,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20602,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20602,], ["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","",20595,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20602,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20595,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20595,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20595,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20595,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20595,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20595,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20595,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20595,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20595,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20595,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20595,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20595,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20595,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20595,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20595,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20602,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20602,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20602,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20602,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20602,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20602,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20602,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20602,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20602,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20602,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20602,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20602,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20602,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20602,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20602,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1308,38 +1322,40 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_7_en","libraries.matrix.ui.messages.sender_SenderName_Night_7_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_8_en","libraries.matrix.ui.messages.sender_SenderName_Night_8_en",0,], -["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20595,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20595,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20595,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20595,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20595,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20595,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20595,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20595,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20595,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20595,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20595,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20595,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20595,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20595,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20595,], -["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20595,], -["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20595,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20602,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20602,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20602,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20602,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20602,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20602,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20602,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20602,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20602,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20602,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20602,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20602,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20602,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20602,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20602,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20602,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20602,], +["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20605,], ["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",20595,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20595,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20595,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20595,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20595,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20595,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20595,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20595,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20595,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20595,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20598,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20595,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20602,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20602,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20602,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20602,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20602,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20602,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20602,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20602,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20602,], +["features.location.impl.show_ShowLocationView_Day_8_en","features.location.impl.show_ShowLocationView_Night_8_en",20605,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20602,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20602,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1349,106 +1365,106 @@ 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",20595,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20602,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20595,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20595,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20595,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20595,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20595,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20602,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20602,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20602,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20602,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20602,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20595,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20595,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20595,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20595,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20595,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20595,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20595,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20595,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20595,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20595,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20595,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20595,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20595,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20595,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20595,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20602,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20602,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20602,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20602,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20602,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20602,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20602,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20602,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20602,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20602,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20602,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20602,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20602,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20602,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20595,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20595,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20595,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20595,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20595,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20595,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20602,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20602,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20602,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20602,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20602,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20602,], ["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",20595,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20602,], ["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",20595,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20602,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20595,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20595,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20595,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20595,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20595,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20595,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20595,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20595,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20595,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20595,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20595,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20595,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20595,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20595,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20595,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20595,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20602,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20602,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20602,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20602,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20602,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20602,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20602,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20602,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20602,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20602,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20602,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20602,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20602,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20602,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20602,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20602,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20595,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20595,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20602,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20602,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1461,17 +1477,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20595,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20595,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20602,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20602,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20595,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20595,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20595,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20602,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20602,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20602,], ["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",20595,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20595,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20602,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20602,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1481,18 +1497,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20602,], ["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",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20602,], ["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,], @@ -1500,18 +1516,18 @@ 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",20595,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20602,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20595,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20595,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20602,], ["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",20595,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20602,], ["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,], @@ -1520,44 +1536,44 @@ 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",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20602,], ["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",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20602,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20602,], ["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","",20595,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20602,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20595,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20602,], ["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",20595,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20602,], ["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",20595,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20595,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20595,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20602,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20602,], ["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",20595,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20595,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20602,], ["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",20595,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20602,], ["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,], @@ -1566,8 +1582,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",20595,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20602,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20602,], ["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,], @@ -1582,8 +1598,8 @@ 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",20595,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20595,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20602,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1606,84 +1622,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20595,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20595,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20602,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20602,], ["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",20595,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20602,], ["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",20595,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20602,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20595,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20595,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20602,], ["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",20595,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20595,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20595,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20595,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20595,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20595,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20602,], ["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","",20595,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20602,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20595,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20595,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20595,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20595,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20602,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20602,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20602,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20602,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20595,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20602,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20595,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20602,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20595,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20595,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20595,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20595,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20595,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20595,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20595,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20595,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20595,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20595,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20595,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20595,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20595,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20602,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20602,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20602,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20602,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20602,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20602,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20602,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20602,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20602,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20602,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20602,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20602,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20602,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20595,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20595,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20602,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20602,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20595,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20602,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], From 4b99e5914e8dc7303a3b535c223039b39096d0fd Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:22:11 +0300 Subject: [PATCH 288/471] Address review --- .../slashcommands/impl/DefaultSlashCommandService.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt index 8234116bb1..6cd8688cad 100644 --- a/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt +++ b/libraries/slashcommands/impl/src/main/kotlin/io/element/android/libraries/slashcommands/impl/DefaultSlashCommandService.kt @@ -99,14 +99,6 @@ class DefaultSlashCommandService( override suspend fun proceedAdmin( slashCommand: SlashCommand.SlashCommandAdmin, ): Result { - if (slashCommand is SlashCommand.ChangeDisplayNameForRoom) { - val canUserChangeDisplayName = withTimeoutOrNull(5.seconds) { - capabilitiesProvider.canChangeDisplayName().getOrNull() - } ?: false - if (!canUserChangeDisplayName) { - return Result.failure(Exception("Changing display name is not allowed")) - } - } return commandExecutor.proceedAdmin( slashCommand = slashCommand, ) From f52fc9c1c2123984522680d6431f503d707703ec Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 14:41:26 +0200 Subject: [PATCH 289/471] Fix image resizing behavior. --- .../preview/imageeditor/AttachmentImageEditorView.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt index 6011b52c41..d580a6376e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorView.kt @@ -316,11 +316,13 @@ private fun BoxScope.CropEditorCanvas( ) { change, dragAmount -> val activeTarget = dragTarget ?: return@detectDragGestures change.consume() + val gestureAreaWidth = imageRect.width.takeIf { it > 0f } ?: size.width.toFloat() + val gestureAreaHeight = imageRect.height.takeIf { it > 0f } ?: size.height.toFloat() onCropRectChange( latestCropRect.applyChange( dragTarget = activeTarget, - deltaX = dragAmount.x / size.width.toFloat(), - deltaY = dragAmount.y / size.height.toFloat(), + deltaX = dragAmount.x / gestureAreaWidth, + deltaY = dragAmount.y / gestureAreaHeight, ) ) } From 80de3e4c5d2e8e87303ebccf769053fc48fa4f32 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 1 Jun 2026 15:11:04 +0200 Subject: [PATCH 290/471] Create log messages from `WebView` just once (#6923) We were creating the same message twice by mistake. Also, check the retrieved message for passwords before formatting the log line instead of after: this way we don't create a new formatted log line to just discard it --- .../browser/ConsoleMessageLogger.kt | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ConsoleMessageLogger.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ConsoleMessageLogger.kt index d165da8640..18d9990c0b 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ConsoleMessageLogger.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/browser/ConsoleMessageLogger.kt @@ -33,6 +33,11 @@ class DefaultConsoleMessageLogger : ConsoleMessageLogger { else -> Log.DEBUG } + // Avoid logging any messages that contain "password" to prevent leaking sensitive information + if (consoleMessage.message().contains("password=")) { + return + } + val message = buildString { append(consoleMessage.sourceId()) append(":") @@ -41,20 +46,6 @@ class DefaultConsoleMessageLogger : ConsoleMessageLogger { append(consoleMessage.message()) } - // Avoid logging any messages that contain "password" to prevent leaking sensitive information - if (message.contains("password=")) { - return - } - - Timber.tag(tag).log( - priority = priority, - message = buildString { - append(consoleMessage.sourceId()) - append(":") - append(consoleMessage.lineNumber()) - append(" ") - append(consoleMessage.message()) - }, - ) + Timber.tag(tag).log(priority = priority, message = message) } } From b68c78526f3b7190819c3cb876f1bfc49119c42c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 15:49:53 +0200 Subject: [PATCH 291/471] Use secondary color (default) for leading icons. Fixes #6910 --- .../features/home/impl/roomlist/RoomListContextMenu.kt | 5 ----- .../features/messages/impl/actionlist/ActionListView.kt | 2 +- .../impl/messagecomposer/AttachmentsBottomSheet.kt | 8 -------- .../impl/root/RolesAndPermissionsView.kt | 1 - .../roommembermoderation/impl/RoomMemberModerationView.kt | 1 - .../userprofile/shared/blockuser/BlockUserSection.kt | 1 - .../matrix/ui/components/AvatarActionBottomSheet.kt | 2 +- .../mediaviewer/impl/details/MediaDetailsBottomSheet.kt | 5 ----- 8 files changed, 2 insertions(+), 23 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt index 66982961fd..03db82f252 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContextMenu.kt @@ -119,7 +119,6 @@ private fun RoomListModalBottomSheetContent( leadingContent = ListItemContent.Icon( iconSource = IconSource.Vector(CompoundIcons.MarkAsRead()) ), - style = ListItemStyle.Primary, ) } else { ListItem( @@ -133,7 +132,6 @@ private fun RoomListModalBottomSheetContent( leadingContent = ListItemContent.Icon( iconSource = IconSource.Vector(CompoundIcons.MarkAsUnread()) ), - style = ListItemStyle.Primary, ) } val (textResId, icon) = if (contextMenu.isFavorite) { @@ -159,7 +157,6 @@ private fun RoomListModalBottomSheetContent( onClick = { onFavoriteChange(!contextMenu.isFavorite) }, - style = ListItemStyle.Primary, ) ListItem( headlineContent = { @@ -174,7 +171,6 @@ private fun RoomListModalBottomSheetContent( CompoundIcons.Settings(), ) ), - style = ListItemStyle.Primary, ) if (canReportRoom) { ListItem( @@ -211,7 +207,6 @@ private fun RoomListModalBottomSheetContent( leadingContent = ListItemContent.Icon( iconSource = IconSource.Vector(CompoundIcons.Delete()) ), - style = ListItemStyle.Primary, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index e10dd2e1bc..c2d97dcf80 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -249,7 +249,7 @@ private fun ActionListViewContent( leadingContent = ListItemContent.Icon(IconSource.Resource(action.icon)), style = when { action.destructive -> ListItemStyle.Destructive - else -> ListItemStyle.Primary + else -> ListItemStyle.Default } ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt index 405e2b0be9..4f7523cbf3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt @@ -34,7 +34,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem -import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text @@ -106,25 +105,21 @@ private fun AttachmentSourcePickerMenu( modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.PhotoFromCamera) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.TakePhoto())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) }, - style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.VideoFromCamera) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.VideoCall())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) }, - style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.FromGallery) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Image())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) }, - style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.PickAttachmentSource.FromFiles) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Attachment())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_files)) }, - style = ListItemStyle.Primary, ) if (state.canShareLocation) { ListItem( @@ -134,7 +129,6 @@ private fun AttachmentSourcePickerMenu( }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.LocationPin())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, - style = ListItemStyle.Primary, ) } ListItem( @@ -144,14 +138,12 @@ private fun AttachmentSourcePickerMenu( }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Polls())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_poll)) }, - style = ListItemStyle.Primary, ) if (enableTextFormatting) { ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvent.ToggleTextFormatting(enabled = true)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.TextFormatting())), headlineContent = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) }, - style = ListItemStyle.Primary, ) } } diff --git a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt index 2f8dab8029..964518da10 100644 --- a/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt +++ b/features/rolesandpermissions/impl/src/main/kotlin/io/element/android/features/rolesandpermissions/impl/root/RolesAndPermissionsView.kt @@ -181,7 +181,6 @@ private fun ChangeOwnRoleBottomSheet( ListItem( headlineContent = { Text(stringResource(CommonStrings.action_cancel)) }, onClick = ::dismiss, - style = ListItemStyle.Primary, ) } } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index 53bf667c6e..08d9143e0b 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -278,7 +278,6 @@ private fun RoomMemberActionsBottomSheet( when (val action = actionState.action) { is ModerationAction.DisplayProfile -> { ListItem( - style = ListItemStyle.Primary, headlineContent = { Text(stringResource(R.string.screen_bottom_sheet_manage_room_member_member_user_info)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile())), onClick = { diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt index c3caffa7f3..62efbf1f0f 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/blockuser/BlockUserSection.kt @@ -84,7 +84,6 @@ private fun PreferenceBlockUser( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), onClick = { if (!isLoading) eventSink(UserProfileEvents.UnblockUser(needsConfirmation = true)) }, trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, - style = ListItemStyle.Primary, ) } else { ListItem( diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index 880a7a9df6..32899a8d2c 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -104,7 +104,7 @@ private fun AvatarActionBottomSheetContent( leadingContent = ListItemContent.Icon(IconSource.Resource(action.iconResourceId)), style = when { action.destructive -> ListItemStyle.Destructive - else -> ListItemStyle.Primary + else -> ListItemStyle.Default } ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt index 0c73bfd17c..1f16391265 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt @@ -105,7 +105,6 @@ fun MediaDetailsBottomSheet( ListItem( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.VisibilityOn())), headlineContent = { Text(stringResource(CommonStrings.action_view_in_timeline)) }, - style = ListItemStyle.Primary, onClick = { onViewInTimeline(state.eventId) } @@ -113,7 +112,6 @@ fun MediaDetailsBottomSheet( ListItem( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ShareAndroid())), headlineContent = { Text(stringResource(CommonStrings.action_share)) }, - style = ListItemStyle.Primary, onClick = { onShare(state.eventId) } @@ -121,7 +119,6 @@ fun MediaDetailsBottomSheet( ListItem( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Forward())), headlineContent = { Text(stringResource(CommonStrings.action_forward)) }, - style = ListItemStyle.Primary, onClick = { onForward(state.eventId) } @@ -129,7 +126,6 @@ fun MediaDetailsBottomSheet( ListItem( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Download())), headlineContent = { Text(stringResource(CommonStrings.action_download)) }, - style = ListItemStyle.Primary, onClick = { onDownload(state.eventId) } @@ -148,7 +144,6 @@ fun MediaDetailsBottomSheet( ListItem( leadingContent = icon, headlineContent = { Text(wording) }, - style = ListItemStyle.Primary, onClick = { onOpenWith(state.eventId) } From 31776a74f7af7ce6385cadaaa17f3a56359ea18c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 1 Jun 2026 14:09:23 +0000 Subject: [PATCH 292/471] Update screenshots --- ...atures.home.impl.roomlist_RoomListContextMenu_Day_0_en.png | 4 ++-- ...atures.home.impl.roomlist_RoomListContextMenu_Day_1_en.png | 4 ++-- ...atures.home.impl.roomlist_RoomListContextMenu_Day_2_en.png | 4 ++-- ...ures.home.impl.roomlist_RoomListContextMenu_Night_0_en.png | 4 ++-- ...ures.home.impl.roomlist_RoomListContextMenu_Night_1_en.png | 4 ++-- ...ures.home.impl.roomlist_RoomListContextMenu_Night_2_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_6_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_7_en.png | 4 ++-- .../snapshots/images/features.home.impl_HomeView_Day_8_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_6_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_7_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_8_en.png | 4 ++-- ...ssages.impl.actionlist_ActionListViewContent_Day_10_en.png | 4 ++-- ...ssages.impl.actionlist_ActionListViewContent_Day_11_en.png | 4 ++-- ...ssages.impl.actionlist_ActionListViewContent_Day_12_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_2_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_3_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_4_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_5_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_6_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_7_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_8_en.png | 4 ++-- ...essages.impl.actionlist_ActionListViewContent_Day_9_en.png | 4 ++-- ...ages.impl.actionlist_ActionListViewContent_Night_10_en.png | 4 ++-- ...ages.impl.actionlist_ActionListViewContent_Night_11_en.png | 4 ++-- ...ages.impl.actionlist_ActionListViewContent_Night_12_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_2_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_3_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_4_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_5_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_6_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_7_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_8_en.png | 4 ++-- ...sages.impl.actionlist_ActionListViewContent_Night_9_en.png | 4 ++-- ...pl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en.png | 4 ++-- ....messagecomposer_AttachmentSourcePickerMenu_Night_0_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Day_1_en.png | 4 ++-- .../images/features.messages.impl_MessagesView_Night_1_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_0_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_1_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_2_en.png | 4 ++-- ...embermoderation.impl_RoomMemberModerationView_Day_3_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_0_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_1_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_2_en.png | 4 ++-- ...bermoderation.impl_RoomMemberModerationView_Night_3_en.png | 4 ++-- .../features.userprofile.shared_UserProfileView_Day_2_en.png | 4 ++-- .../features.userprofile.shared_UserProfileView_Day_5_en.png | 4 ++-- ...features.userprofile.shared_UserProfileView_Night_2_en.png | 4 ++-- ...features.userprofile.shared_UserProfileView_Night_5_en.png | 4 ++-- ....matrix.ui.components_AvatarActionBottomSheet_Day_0_en.png | 4 ++-- ...atrix.ui.components_AvatarActionBottomSheet_Night_0_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png | 4 ++-- ...ies.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png | 4 ++-- ...s.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png | 4 ++-- ...ibraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png | 4 ++-- 61 files changed, 122 insertions(+), 122 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_0_en.png index 6b4037b3eb..83ee2f4342 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c755a7dcfc2c9f48d449e570a3f1af1cde299fd90ddd4478e9a4c315cf03128 -size 23810 +oid sha256:aec4d2f2ec1b277afa895e47739b503d86b3104d315b6d20fce223324c57f51b +size 23960 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_1_en.png index e64ae4c3cf..84b0af9394 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efa5408c3af0fcd39659b8d30b21ace43be46d2876815ad592f802a7887b5eb9 -size 23678 +oid sha256:82f48053958a8bd72245365ee0cc4c1649c5c06dde6f66c95c23bf5009edd2f3 +size 23879 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_2_en.png index 82b04fc045..d7fec262ca 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fc512e71e168473e0e976be3e3e6cd24b455273257ad0adf970fc2641da43bb -size 25679 +oid sha256:e50eaee3dc1512f76566d1170c2db8a07230d7c9f159c5f52c43ac9e810e2a72 +size 25938 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_0_en.png index f52d785548..8a06c99645 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de71af50bba66a285b5ef44e1a4fe76ce6dc2e094c4c09ddf701deb9a5db898d -size 22025 +oid sha256:3abd359bba25e19e231d723cc3de7dcf394c35a5948a935b2703d81940e00120 +size 22391 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_1_en.png index eef2177c3b..9fa6aa895a 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88b3583d55c6855027f13a152a6fc414f6b9e11d65e5b5403463b84fafe38c3f -size 21891 +oid sha256:4910c5904a53dacc578b32d0033e3fe64fa084602b62dff13930b68ee83e0cf5 +size 22298 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_2_en.png index 87d3f56720..f7c703f8ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.roomlist_RoomListContextMenu_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e64367832735139b68269a8c679b9c6f2b929b906124a0ee93735cbdd9202d6d -size 23826 +oid sha256:82ed1ed8042d4d6f9633144cc5f34567cfc09c6303e005a7157954917837054d +size 24160 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png index 99940a4489..807a0404c4 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e74b9b1819bf778ddb397a40113f71b2eb0ada0dcb26af85dab26bbc53ed079 -size 54210 +oid sha256:6833382305574e875ab2b5cbfa0ae441d06e405ae64f69a8b9de6e2f3da96922 +size 54461 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png index 2532044b27..102ab5d046 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4e56509b4f10e97283d25432edd7be4b511987f0879acc3026ac16dab25b75c -size 54012 +oid sha256:45d4703616e98579df79f9d23f1f0c954f937b75f7eeab1452675b8eaa3c0770 +size 54266 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png index 0baf9cf023..d133b74182 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe76cc652842cce967d975fae7a387f5cff118153b345bda452d5dbc618b22f8 -size 52255 +oid sha256:ea4983028fd8b8044df7d87125d49346bca92920217f14b7ce2aaac9dbe8af4b +size 52534 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png index ead9ed0d6d..6dfedf5331 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2d17c9021392f93fc022ce0ba4305f0739acaeab3ebfd51b7d3294ace365807 -size 51156 +oid sha256:86cd78d9ac357bfcbc017440b57e1d643255b6ffbe47e78c3df197405c185495 +size 51522 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png index 33f2b46a34..4260283a5d 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:286af36457c34621e10b91226b13c5a9fde77af6b63827d39a0a95697972d4ab -size 50896 +oid sha256:ff899eb696998139c0b28b1596aa71fcb477cc534892bbfdeb81405412a6121b +size 51286 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png index 733a66bb38..abd2fd13fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34a5fe14ee5ede42d962e657e4338f58f56e79e0f9a3f65ba24e37810a9c1250 -size 49205 +oid sha256:73d1f08075d303b49e5f3b76f06744e59620eddd6dd45c8715fb2ec21c82e568 +size 49555 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png index 3783974c39..96240c9b67 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9912a8d83fd78d27fa6b01fd9034a03e50bc598f10330681c51fc776d5541bce -size 29420 +oid sha256:eeef211d5fb4e9474545ce91ddc7470703a1e04a0ea25048eada1d5700c62f90 +size 29595 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png index f594f885e0..c3a33f959d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab09bad33db524c5bf21815b0573cce94a5a04a3cb1a7c5eac08788a6fd43bc6 -size 48565 +oid sha256:34dc91f10333b05fb7d25293847e8ef8cc455ac416f95a57e7c21fa107f17924 +size 48938 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png index 336b672698..2eb5dd6e27 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:028dc1af0a32614235945f29b86fc75f7e4101115ca6bb24a8519d9b540e3446 -size 50076 +oid sha256:447c10f67637776e73fbb73a9d38cccf6dd5dc6629b84b95230099c0466f489d +size 50451 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png index 5a7092ba16..cb7e98ab69 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:050194b3070ce4e062b419467c0429b4ec025bda070dbf9499ebf22094d930e1 -size 43582 +oid sha256:9d9f5edcae6f3d64b7ee92b2fdde6a78fd4535e001a590c0ba0f31658d9bbfcb +size 43748 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png index 8888dbdfcb..a6bb00e532 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:979969357697032a7b7e5e715f42434cee98e6ad6eed439baf79ca0b972cef42 -size 46476 +oid sha256:4ec56befb7c774bf4545b8ee56c392a2b7a83511efb524703bb44659ace359fa +size 46839 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png index f1b598d66b..94ff687fbb 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6ca9ef59f9e436190c1342dcc0376c88bf46c8b6b2f84b6a487d3bf998e94bc -size 44877 +oid sha256:13ca2d6273c2e9b76271ab42aa2e435fc7547bd675d29113f6a285da91c7efbe +size 45100 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png index d70c672fda..c2165768a6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb661596edcdda0d2a5f57c72a396624ab6f4291d07c0224cdcdeba67fa48071 -size 41955 +oid sha256:e6401ce52238a6f7f874993713139b0b4bbb435a24931b95dd2be0cc904a40e4 +size 42210 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png index cb1af3bb6e..d49b066f2a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:834245a21eb2bb644b5d9afda81346c37a3a6121d20d5c14c562b100cc5b913c -size 45046 +oid sha256:b61cdf6910a74a2d2593c95dee752a3e4f5b4b9288227544a7b4aaa813fee89e +size 45417 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png index 8b6616fc18..ac761dc5d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bb9f6d15e6556e692a6d879202bf6036d9cf1b353f317b911cc7ca379c2a90e -size 43190 +oid sha256:4cff72a2042e4fdf61d09eb118e5d14bea1fd78533b2540356374e3641eb9719 +size 43445 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png index 02318d8ce5..2102db6ed8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e01aa32704fb8d1f2b7f4325f94635b345ce3117e384923e8132a68b566d8968 -size 44964 +oid sha256:bd07b8f9652520c7819689908e5067589f9b02aae28497ea417e896d1976b6a9 +size 45208 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png index 9b0b7bfe4f..5a3dbf94f9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45eb96b553465eaccc2e5081bb8b867a8cd2d4e5ed1ce66767828bfb3b7fc0fc -size 34176 +oid sha256:e54261b95e0bd072ab51e289f48dafb611dfa0eed1f30bd1e8f964dfe15b8656 +size 34516 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png index 6170e24768..111dbec964 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9a6d982e6a414650a917da3e66b2155f8b6ce3285ca4fefd4afcd0df7f6184c -size 28432 +oid sha256:dbf8d20a5de034e5487dd6a53e112995abf15c7d2f3da132ab5d25e8a90ac3d0 +size 28882 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png index cb440db48d..35034a2a18 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06180ecb1a875d6d007a528730d53d4913c733d8b1218ee69daa3df557130806 -size 47781 +oid sha256:f241204e1bf54222cc82b8a8f85278e347a5978308ea48fae4231ccf61950845 +size 48275 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png index dba9e6d4c8..97c55b2bef 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:670bbfdc5ae48ba97911716db461ab391a57e94764508eae88c594b68e96e826 -size 49278 +oid sha256:3fddb43c6de6277f396d82755650136304c310d8f5607fb5b87d6d9c03a5ed78 +size 49754 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png index dfccd98a28..82b82ae51e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0d0257a4d2eed51e9c18f3afbe0e39fffb7440e0ce749eec8959c9b4e5e063a -size 42723 +oid sha256:de93101b9a5c06d0e31b9ac83f34a3db18e8a98f15d3bd2c62e13d4c3bb8074b +size 43187 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png index 4022cb1368..c2ef33efd4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05e6093a0a7e2387c2c06270416e53c0654353cd1e348829c31f1377c618374f -size 45875 +oid sha256:e4f77edfdf5c82a807a5a4b8aecbd391da8b403082caade04c53b6ac09adeea6 +size 46293 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png index 7f7642064e..61a1a6b088 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b021dfc67025bf99ca1b6ad8b28afcb29611716635a57f4c3e2b250515f63da6 -size 44357 +oid sha256:a359101a1b23fb039d30060f7eb49c8e0188594ae2810a209fcdec0e68984245 +size 44755 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png index dc562de97b..b6b850a8b1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:199ae1aa490d6b7c3e4e0a2c4efedc5fd8f9140d18be74baf9b67023f9bc2165 -size 40888 +oid sha256:3a496227e12ffd07749a13ff805d7e385bedd04c6aebcd527a9815709636ee8c +size 41378 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png index e79cd5a5d5..54e2d716bd 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b5a85043479aff4f13fe1b2e86ad4700410dd9c57219659c0b2cf93acfdc6a2 -size 44679 +oid sha256:853a39c07503f5878a52d99e3d076bdf929b7867339b28bab73ecc0ea764eb1a +size 45041 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png index 4825735975..d3b6d14e50 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bcf537c2f4d01fef81785ddc0b8f5133e2bc1966e6837e239ccfc0a06f14d18 -size 42111 +oid sha256:a569526be5f0568a6b9631006b9f08ccde11190b6f3ab5cee345ba991e7d1a2f +size 42554 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png index 225a8b94d7..e6a5be294c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f27444a111e07658820b19dcc780219ea80b2ea5d01495ecee1255c0e84650e -size 44507 +oid sha256:d710b57a36ba6d73dc1b81ff2189ee36d0314c5ca1ab9da2f2f54cf3bcc3bc55 +size 44867 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png index 46c4f1942e..d9f2017597 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.actionlist_ActionListViewContent_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbe4516b52f897a4f91d229f3a0f1bcd3a867d9494ac894d11452bcdf9407f2c -size 33013 +oid sha256:69fc6332d8c7607b14dc7c2ad595d545f83f52cc0e42da5fee05f127fed5713a +size 33424 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en.png index 303a32a04d..8f4e2dfe50 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36926eca7b934f7794ddd73e7929861d8837e9ac51341520c883043dc53d1927 -size 25026 +oid sha256:bfc50e8997f52f2b0b808af90b09000a27c9943505460692fbe541a3bd95c440 +size 25622 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en.png index 32c4015a76..862e26b1d6 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:493ed64dc981851887506ce33cdd063a107c8d5e6376027e7949a248b88e2a42 -size 24167 +oid sha256:4faa8dc1a367649a4a862e8647e733d77dd31737b34f9a8a5997f2345dab50a0 +size 25035 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 ef8699b324..9685e6cbc9 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:f21b1799dec6cb466378a748adb5227f76bd6cf611be694922ef63a951fdf692 -size 39910 +oid sha256:d1a28720d554bc4698eb52e27e7c6228ac9c14b3703a8c139c3113f8b8fa284e +size 40355 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 51a300b462..d7eb027558 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:84a6b1ef879f1a9be16526efdd546aa554e03ea74b0e8ec03178b2a860b752e5 -size 38089 +oid sha256:5789d10db607ad77acd5d9391b970b2e0a0f962c205b3bf40db5c78d0dcea48b +size 38821 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png index b5ff7ebce2..adc8e30fdb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49ecd29e87c23ac92f0b5775e95a1b46dda41bbd7bb52a5cbfbcf7cd039824c9 -size 17240 +oid sha256:8b15da251ba8ac26c61fd21079f09e3b614fd20e8ded4805cda0da180631afcc +size 17347 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png index ae186ece30..1e3ab791b8 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ac03bbed84ae266088d0e487e36ac30ddaac22bdaea6218423a8a41b77f4c5b -size 20217 +oid sha256:fc4a3ba905c3599ad8f6451787906ba61f8dc716464d81e8c1c1bc86023032c3 +size 20345 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png index 6311df6e6d..5253ae2439 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d44e5119301f217903d1ca232f17ae54ba96da9a6878e668f603c57bcc23761 -size 22612 +oid sha256:5704d02932451bc129ec92f3a9dab7fd66bb55bfdf3e142a4f3a436ad1d3b129 +size 22722 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png index 90b562aa09..b905c2d8cb 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:980b9744a36174bb8b080a431b277b544dfc045869102147fb9d1186e396b5e9 -size 22720 +oid sha256:8755fa3568186a798047a7fbf05404c0f738b168ca77f16afecc815bbec084ab +size 22828 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png index 1a59277355..f4922aad1c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ebad5a2935050fa87c16c518323bdf3db4494fd8a14a5a03809e3e51f047b70 -size 16318 +oid sha256:ba469dd7ffb3b27261cc4f227584b7cc03bf2ddcb974a4a1150c9515a075ac78 +size 16562 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png index 9e8644ac62..c79de434d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b427494aa12528f795a49f27912b90ebb59392be0420fc27e3c4014a3599680 -size 19155 +oid sha256:d21c293e519e998d8820171766f1651d2440b18dad6f002c6580ca3fe2f24d69 +size 19397 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png index 01b60a339f..dfa20ea763 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d301576daafc3b86949208e281479183558d61cbadeb412007ee8436aa7eebb9 -size 21590 +oid sha256:24a1a6004dc058573f264a5f02ffc85ca925e9f0c01f7ceacbaf972b3335d327 +size 21819 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png index b96d820784..07547bf79a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc69bdbe8ac22e7d6470a8b547ba364e3421f6d61ccd0c364492dd860b1ce093 -size 21682 +oid sha256:cf93cbb2ce344639162524714d254c0db21cab448dec38ee1d4820265678a7ec +size 21909 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_2_en.png index 7951440a2f..3419a50b07 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8185065602221f9bf675398d42c16f9e0c7ae65952b865689263878f43c545d -size 24110 +oid sha256:9973d9a9fa00d362eae8f50c2936e5ae0adc2653b4671413b856c1e73538eb73 +size 24261 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_5_en.png index 12563b1b76..7e6ca5d507 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c3ff3444b2f720b09de118af05e1376001bf3cfe9be66acec4a68cecfe9ae6b -size 22357 +oid sha256:4a4154169fd6e74358001ecc151a0cb4635423bf4a0b2d72f1d5bb12dfb0ea95 +size 22446 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_2_en.png index 61d82d6084..224210123e 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2f2787f0aacfa36596b9fa0e25d990710870bf5ed25d831b47c5c71ac1d3de9 -size 23377 +oid sha256:e28754f90d584c329a5e3c51eba89c166320c69fda309b6b06b6f5ee9c74c1b2 +size 23549 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_5_en.png index 90376bc952..408be83664 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e7e43be60d1a545621d9d64ae3500980056f2c405c8d22629ab63e149aad224 -size 21816 +oid sha256:3b815d4cf8ed95cb983072d75419637c10bd4808ddfbf14865409d0a4fd64c37 +size 22070 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 f5d8e0173e..362e750080 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:368aec8db00c98f5e2b73763826f484c4ca395bfee9156cc7796029f57ceb907 -size 13464 +oid sha256:7a804cd9678ae0aa149065dfc7ce98bacb30956ac75a17f80a7f32acbbb6681c +size 13598 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 e16de72924..1aaf062713 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:707eaf1db064e542041746c1fff6f81a47e95a45579a9738f7f02573ce6db0f8 -size 12261 +oid sha256:44fa03d304319f972c0e9c6e297e4b86bd0603a90945126f47f657d566241911 +size 12500 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png index 8a527331d9..c187b29009 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67be1e8e93d0c10366f4b37f14ca0f90c1f4ccd5da9024c9a396d3d46507e18c -size 40339 +oid sha256:be6223e16a2b9936164ec4ba659129ceb80ed5746e3376f32559b4942b271502 +size 40656 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png index 8a527331d9..c187b29009 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67be1e8e93d0c10366f4b37f14ca0f90c1f4ccd5da9024c9a396d3d46507e18c -size 40339 +oid sha256:be6223e16a2b9936164ec4ba659129ceb80ed5746e3376f32559b4942b271502 +size 40656 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png index d53b0ecf7e..b95ce472ff 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f89a1039bc2d101af319c0b6259d8eebc9d313ae34947aedee525945be23799 -size 44604 +oid sha256:b57d412a5815756b453d907543bc74b4a154814633d8664a3dcffaa0057c7279 +size 44962 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png index e16a2bc691..a86d65d8f8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a512016e96b933f6089e60b634fa77f140dfa7de50d3a8c1a62092ad552d7f2d -size 39216 +oid sha256:5e3e43f4dbd1bf0e271de57c962b93100720439d608a7d552669ede42141dbc1 +size 39705 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png index e16a2bc691..a86d65d8f8 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a512016e96b933f6089e60b634fa77f140dfa7de50d3a8c1a62092ad552d7f2d -size 39216 +oid sha256:5e3e43f4dbd1bf0e271de57c962b93100720439d608a7d552669ede42141dbc1 +size 39705 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png index e36f282ba2..782df4dbaa 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8608e63ef9ff79fd698c202d60a8c73f8874f8610601a0ff6dc85663ed82c7da -size 43537 +oid sha256:086c64f4094cb2e0fda8e415a0d8d89b7dafe67f4d42088eded7abbf45b8de93 +size 44088 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png index 809459297c..6c70063773 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ddbddbf5303bb86f901cdd880ca2fde15c7c4af22dd7cf1a7903dcab5df25b3 -size 40254 +oid sha256:ad4bf14c2f6d2d36c978d90c7641fc8b3a9c71062d6b23362eea12c0b7ff24e4 +size 40572 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png index 1ceb7c6b68..40d55d29a1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89e17d0b8fdd749b6e97385e8369132d52b0e10adf7d23157c3bdbeb7721e694 -size 39007 +oid sha256:7b44ce089c932019d58e7f1c993f80871352878adf0f5a133a72edce18f5bcb9 +size 39496 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png index 1ceb7c6b68..40d55d29a1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89e17d0b8fdd749b6e97385e8369132d52b0e10adf7d23157c3bdbeb7721e694 -size 39007 +oid sha256:7b44ce089c932019d58e7f1c993f80871352878adf0f5a133a72edce18f5bcb9 +size 39496 From 1df94a563c53628ee931a66c443175210e27da26 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 1 Jun 2026 17:25:33 +0200 Subject: [PATCH 293/471] Use reverse ordering for `FilterEmptyDayPostProcessor` (#6927) It turns out the previous ordering was right: timestamp ordering is ascending, not descending --- .../FilterEmptyDayPostProcessor.kt | 8 ++++-- .../FilterEmptyDayPostProcessorTest.kt | 28 +++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt index b7dceafa25..b4d21b36be 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessor.kt @@ -18,10 +18,10 @@ class FilterEmptyDayPostProcessor { * Filters out day separators from [items] for days that don't contain any events. */ fun process(items: List): List = buildList { - // The timeline is ordered by descending timestamp, so events that happened during a day appear before the day separator for that day. - // We can use this to determine if a day separator should be kept or not. + // The timeline is ordered by ascending timestamp, so events that happened during a day appear after the day separator for that day. + // We can use this to determine if a day separator should be kept or not by traversing the list in reverse. var hasEvent = false - for (item in items) { + for (item in items.asReversed()) { if (item is MatrixTimelineItem.Event) { hasEvent = true add(item) @@ -35,4 +35,6 @@ class FilterEmptyDayPostProcessor { } } } + // Then reverse the result to restore the original order, minus the empty day separators. + .asReversed() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt index 7a3a091f4e..67d7e3621d 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/FilterEmptyDayPostProcessorTest.kt @@ -32,36 +32,36 @@ class FilterEmptyDayPostProcessorTest { @Test fun `filterEmptyDaySeparators keeps day separator with events after it`() { val items = listOf( - anEvent, aDaySeparator(TODAY), + anEvent, ) val result = FilterEmptyDayPostProcessor().process(items) assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(anEvent) - assertThat(result[1]).isEqualTo(aDaySeparator(TODAY)) + assertThat(result[0]).isEqualTo(aDaySeparator(TODAY)) + assertThat(result[1]).isEqualTo(anEvent) } @Test fun `filterEmptyDaySeparators removes day separator with no events after it`() { val items = listOf( - aDaySeparator(TODAY), aDaySeparator(YESTERDAY), + aDaySeparator(TODAY), ) val result = FilterEmptyDayPostProcessor().process(items) assertThat(result).isEmpty() } @Test - fun `filterEmptyDaySeparators removes first day separator and keeps second when only second has events`() { + fun `filterEmptyDaySeparators removes second day separator and keeps first when only first has events`() { val items = listOf( - aDaySeparator(TODAY), - anEvent, aDaySeparator(YESTERDAY), + anEvent, + aDaySeparator(TODAY), ) val result = FilterEmptyDayPostProcessor().process(items) assertThat(result).hasSize(2) - assertThat(result[0]).isEqualTo(anEvent) - assertThat(result[1]).isEqualTo(aDaySeparator(YESTERDAY)) + assertThat(result[0]).isEqualTo(aDaySeparator(YESTERDAY)) + assertThat(result[1]).isEqualTo(anEvent) } @Test @@ -78,15 +78,15 @@ class FilterEmptyDayPostProcessorTest { @Test fun `filterEmptyDaySeparators keeps all items when no day separators`() { val items = listOf( - anEvent, anEvent.copy(uniqueId = UniqueId("event2")), + anEvent, ) val result = FilterEmptyDayPostProcessor().process(items) assertThat(result).hasSize(2) } @Test - fun `filterEmptyDaySeparators removes day separator followed by non-event virtual item`() { + fun `filterEmptyDaySeparators removes day separator preceded by non-event virtual item`() { val readMarker = MatrixTimelineItem.Virtual( uniqueId = UniqueId("readMarker"), virtual = VirtualTimelineItem.ReadMarker @@ -107,12 +107,12 @@ class FilterEmptyDayPostProcessorTest { virtual = VirtualTimelineItem.ReadMarker ) val items = listOf( - anEvent, - readMarker, aDaySeparator(TODAY), + readMarker, + anEvent, ) val result = FilterEmptyDayPostProcessor().process(items) assertThat(result).hasSize(3) - assertThat(result[2]).isEqualTo(aDaySeparator(TODAY)) + assertThat(result[0]).isEqualTo(aDaySeparator(TODAY)) } } From 6222416b1764445b7e010803f3308bb9187f2bdd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 17:34:36 +0200 Subject: [PATCH 294/471] Romo details: reorder items Fixes #6824 --- .../roomdetails/impl/RoomDetailsPresenter.kt | 4 +- .../roomdetails/impl/RoomDetailsState.kt | 2 +- .../impl/RoomDetailsStateProvider.kt | 6 +- .../roomdetails/impl/RoomDetailsView.kt | 149 +++++++++--------- .../roomdetails/impl/RoomDetailsViewTest.kt | 4 +- .../preview/PreviewWithLargeHeight.kt | 3 + 6 files changed, 85 insertions(+), 83 deletions(-) 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 e6617d1dd5..6a88350360 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 @@ -174,7 +174,7 @@ class RoomDetailsPresenter( } } - val roomMemberDetailsState = roomMemberDetailsPresenter?.present() + val dmOtherMemberDetailsState = roomMemberDetailsPresenter?.present() val hasMemberVerificationViolations by produceState(false) { room.roomMemberIdentityStateChange(waitForEncryption = true) @@ -196,7 +196,7 @@ class RoomDetailsPresenter( canEdit = roomType == RoomDetailsType.Room && permissions.editDetailsPermissions.hasAny, roomCallState = roomCallState, roomType = roomType, - roomMemberDetailsState = roomMemberDetailsState, + dmOtherMemberDetailsState = dmOtherMemberDetailsState, leaveRoomState = leaveRoomState, roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(), isFavorite = isFavorite, 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 bd3b90d416..4464058701 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 @@ -31,7 +31,7 @@ data class RoomDetailsState( val memberCount: Long, val isEncrypted: Boolean, val roomType: RoomDetailsType, - val roomMemberDetailsState: UserProfileState?, + val dmOtherMemberDetailsState: UserProfileState?, val canEdit: Boolean, val canInvite: Boolean, val roomCallState: RoomCallState, 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 7182d2ec04..935b7293ae 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 @@ -106,7 +106,7 @@ fun aRoomDetailsState( canEdit: Boolean = false, roomCallState: RoomCallState = aStandByCallState(), roomType: RoomDetailsType = RoomDetailsType.Room, - roomMemberDetailsState: UserProfileState? = null, + dmOtherMemberDetailsState: UserProfileState? = null, leaveRoomState: LeaveRoomState = aLeaveRoomState(), roomNotificationSettings: RoomNotificationSettings = aRoomNotificationSettings(), isFavorite: Boolean = false, @@ -137,7 +137,7 @@ fun aRoomDetailsState( canEdit = canEdit, roomCallState = roomCallState, roomType = roomType, - roomMemberDetailsState = roomMemberDetailsState, + dmOtherMemberDetailsState = dmOtherMemberDetailsState, leaveRoomState = leaveRoomState, roomNotificationSettings = roomNotificationSettings, isFavorite = isFavorite, @@ -184,7 +184,7 @@ fun aDmRoomDetailsState( isEncrypted = isEncrypted, canInvite = true, roomType = RoomDetailsType.Dm(otherMember = aDmRoomMember(isIgnored = isDmMemberIgnored)), - roomMemberDetailsState = aUserProfileState( + dmOtherMemberDetailsState = aUserProfileState( isBlocked = AsyncData.Success(isDmMemberIgnored), verificationState = dmRoomMemberVerificationState, ) 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 40210279fa..27eba6f91f 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 @@ -43,6 +43,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.roomcall.api.hasPermissionToJoin +import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection @@ -64,7 +65,7 @@ import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight +import io.element.android.libraries.designsystem.preview.PreviewWithExtraLargeHeight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem @@ -229,66 +230,11 @@ fun RoomDetailsView( ) } } + // Room content PreferenceCategory { - if (state.roomNotificationSettings != null) { - NotificationItem( - isDefaultMode = state.roomNotificationSettings.isDefault, - openRoomNotificationSettings = openRoomNotificationSettings - ) - } - - FavoriteItem( - isFavorite = state.isFavorite, - onFavoriteChanges = { - state.eventSink(RoomDetailsEvent.SetFavorite(it)) - } + MediaGalleryItem( + onClick = openMediaGallery ) - - if (state.canShowSecurityAndPrivacy) { - SecurityAndPrivacyItem( - onClick = onSecurityAndPrivacyClick - ) - } - } - - state.roomMemberDetailsState?.let { dmMemberDetails -> - if (state.canInvite) { - PreferenceCategory { - InviteItem(onClick = invitePeople) - } - } - PreferenceCategory { - ProfileItem( - verificationState = dmMemberDetails.verificationState, - onClick = { onProfileClick(dmMemberDetails.userId) } - ) - } - } - - if (state.roomType is RoomDetailsType.Room) { - PreferenceCategory { - MembersItem( - memberCount = state.memberCount, - hasVerificationViolations = state.hasMemberVerificationViolations, - openRoomMemberList = openRoomMemberList, - ) - if (state.canShowKnockRequests) { - KnockRequestsItem( - knockRequestsCount = state.knockRequestsCount, - onKnockRequestsClick = onKnockRequestsClick - ) - } - if (state.displayRolesAndPermissionsSettings) { - ListItem( - headlineContent = { Text(stringResource(R.string.screen_room_details_roles_and_permissions)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())), - onClick = openAdminSettings, - ) - } - } - } - - PreferenceCategory { PinnedMessagesItem( pinnedMessagesCount = state.pinnedMessagesCount, onPinnedMessagesClick = onPinnedMessagesClick @@ -296,23 +242,72 @@ fun RoomDetailsView( PollsItem( openPollHistory = openPollHistory ) - MediaGalleryItem( - onClick = openMediaGallery + } + when (state.roomType) { + is RoomDetailsType.Room -> { + PreferenceCategory { + MembersItem( + memberCount = state.memberCount, + hasVerificationViolations = state.hasMemberVerificationViolations, + openRoomMemberList = openRoomMemberList, + ) + if (state.canShowKnockRequests) { + KnockRequestsItem( + knockRequestsCount = state.knockRequestsCount, + onKnockRequestsClick = onKnockRequestsClick + ) + } + if (state.displayRolesAndPermissionsSettings) { + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_details_roles_and_permissions)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Admin())), + onClick = openAdminSettings, + ) + } + } + } + is RoomDetailsType.Dm -> { + if (state.canInvite) { + // Note: for rooms the invite action is a Main action + PreferenceCategory { + InviteItem(onClick = invitePeople) + } + } + state.dmOtherMemberDetailsState?.let { dmMemberDetails -> + PreferenceCategory { + ProfileItem( + verificationState = dmMemberDetails.verificationState, + onClick = { onProfileClick(dmMemberDetails.userId) } + ) + } + } + } + } + PreferenceCategory { + if (state.roomNotificationSettings != null) { + NotificationItem( + isDefaultMode = state.roomNotificationSettings.isDefault, + openRoomNotificationSettings = openRoomNotificationSettings + ) + } + FavoriteItem( + isFavorite = state.isFavorite, + onFavoriteChanges = { + state.eventSink(RoomDetailsEvent.SetFavorite(it)) + } ) + if (state.canShowSecurityAndPrivacy && state.roomType is RoomDetailsType.Room) { + SecurityAndPrivacyItem( + onClick = onSecurityAndPrivacyClick + ) + } } - - if (state.roomType is RoomDetailsType.Dm && state.roomMemberDetailsState != null) { - val roomMemberState = state.roomMemberDetailsState - BlockUserSection(roomMemberState) - BlockUserDialogs(roomMemberState) - } - OtherActionsSection( + dmOtherMemberDetailsState = state.dmOtherMemberDetailsState, canReportRoom = state.canReportRoom, onReportRoomClick = onReportRoomClick, onLeaveRoomClick = { state.eventSink(RoomDetailsEvent.LeaveRoom(needsConfirmation = true)) } ) - if (state.showDebugInfo) { DebugInfoSection( roomId = state.roomId, @@ -803,9 +798,13 @@ private fun OtherActionsSection( canReportRoom: Boolean, onReportRoomClick: () -> Unit, onLeaveRoomClick: () -> Unit, + dmOtherMemberDetailsState: UserProfileState?, ) { - PreferenceCategory(showTopDivider = true) { - if (canReportRoom) { + PreferenceCategory { + if (dmOtherMemberDetailsState != null) { + BlockUserSection(dmOtherMemberDetailsState) + BlockUserDialogs(dmOtherMemberDetailsState) + } else if (canReportRoom) { ListItem( headlineContent = { Text(stringResource(CommonStrings.action_report_room)) @@ -832,7 +831,7 @@ private fun DebugInfoSection( roomVersion: String?, ) { val context = LocalContext.current - PreferenceCategory(showTopDivider = true) { + PreferenceCategory { val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) ListItem( headlineContent = { @@ -870,17 +869,17 @@ private fun DebugInfoSection( } } -@PreviewWithLargeHeight +@PreviewWithExtraLargeHeight @Composable internal fun RoomDetailsPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewLight { ContentToPreview(state) } -@PreviewWithLargeHeight +@PreviewWithExtraLargeHeight @Composable internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewDark { ContentToPreview(state) } -@PreviewWithLargeHeight +@PreviewWithExtraLargeHeight @Composable internal fun RoomDetailsA11yPreview() = ElementPreview { ContentToPreview( diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 9494df5d44..4e0640953b 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -334,7 +334,7 @@ class RoomDetailsViewTest { setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), - roomMemberDetailsState = aUserProfileState(userId = A_USER_ID), + dmOtherMemberDetailsState = aUserProfileState(userId = A_USER_ID), ), onProfileClick = callback, ) @@ -352,7 +352,7 @@ class RoomDetailsViewTest { roomType = RoomDetailsType.Dm( aDmRoomMember(userId = UserId("@other:local.org")), ), - roomMemberDetailsState = aUserProfileState(userId = A_USER_ID), + dmOtherMemberDetailsState = aUserProfileState(userId = A_USER_ID), canInvite = true, ), invitePeople = callback, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt index 163ee21590..8d745ec0e7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewWithLargeHeight.kt @@ -16,3 +16,6 @@ import androidx.compose.ui.tooling.preview.Preview */ @Preview(heightDp = 1000) annotation class PreviewWithLargeHeight + +@Preview(heightDp = 1600) +annotation class PreviewWithExtraLargeHeight From ac93c6bd7044dae052e66603e803d3d15ffadbbb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 17:38:24 +0200 Subject: [PATCH 295/471] Move logic to the presenter --- .../android/features/roomdetails/impl/RoomDetailsPresenter.kt | 2 +- .../android/features/roomdetails/impl/RoomDetailsView.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 6a88350360..b916fa7188 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 @@ -209,7 +209,7 @@ class RoomDetailsPresenter( knockRequestsCount = knockRequestsCount, canShowSecurityAndPrivacy = canShowSecurityAndPrivacy, hasMemberVerificationViolations = hasMemberVerificationViolations, - canReportRoom = canReportRoom, + canReportRoom = !isDm && canReportRoom, isTombstoned = roomInfo.successorRoom != null, showDebugInfo = isDeveloperModeEnabled, roomVersion = roomInfo.roomVersion, 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 27eba6f91f..88aaffc573 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 @@ -804,7 +804,8 @@ private fun OtherActionsSection( if (dmOtherMemberDetailsState != null) { BlockUserSection(dmOtherMemberDetailsState) BlockUserDialogs(dmOtherMemberDetailsState) - } else if (canReportRoom) { + } + if (canReportRoom) { ListItem( headlineContent = { Text(stringResource(CommonStrings.action_report_room)) From d7788a8c014da75681fa1eb62e9ec52f08f2dc04 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 17:41:29 +0200 Subject: [PATCH 296/471] Remember presenter only on userId. Avoid creating again the presenter when other parameter of the room member are changing. --- .../features/roomdetails/impl/RoomDetailsPresenter.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 b916fa7188..9a602d06c0 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 @@ -38,6 +38,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDis import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.JoinedRoom @@ -117,7 +118,7 @@ class RoomDetailsPresenter( val canonicalAlias by remember { derivedStateOf { roomInfo.canonicalAlias } } val isEncrypted by remember { derivedStateOf { roomInfo.isEncrypted == true } } val dmMember by room.getDirectRoomMember(membersState) - val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) + val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember?.userId) val roomType = getRoomType(dmMember) val roomCallState = roomCallStatePresenter.present() val joinedMemberCount by remember { derivedStateOf { roomInfo.joinedMembersCount } } @@ -220,9 +221,9 @@ class RoomDetailsPresenter( } @Composable - private fun roomMemberDetailsPresenter(dmMemberState: RoomMember?) = remember(dmMemberState) { - dmMemberState?.let { roomMember -> - roomMembersDetailsPresenterFactory.create(roomMember.userId) + private fun roomMemberDetailsPresenter(userId: UserId?) = remember(userId) { + userId?.let { userId -> + roomMembersDetailsPresenterFactory.create(userId) } } From 6c804e734d3957addfe1bf24dd651dc914539b6d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 17:46:25 +0200 Subject: [PATCH 297/471] Add missing "Security" section. --- .../features/roomdetails/impl/RoomDetailsView.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 88aaffc573..3d9d41d4c3 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 @@ -302,6 +302,21 @@ fun RoomDetailsView( ) } } + if (state.isEncrypted) { + PreferenceCategory( + title = stringResource(R.string.screen_room_details_security_title) + ) { + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())), + headlineContent = { + Text(stringResource(id = R.string.screen_room_details_encryption_enabled_title)) + }, + supportingContent = { + Text(stringResource(id = R.string.screen_room_details_encryption_enabled_subtitle)) + }, + ) + } + } OtherActionsSection( dmOtherMemberDetailsState = state.dmOtherMemberDetailsState, canReportRoom = state.canReportRoom, From ee4e4ee701e77765fa8caa7781d2c74493e962b8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 17:48:21 +0200 Subject: [PATCH 298/471] Add Figma link --- .../android/features/roomdetails/impl/RoomDetailsView.kt | 3 +++ 1 file changed, 3 insertions(+) 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 3d9d41d4c3..591ff7ff1e 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 @@ -97,6 +97,9 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList +/** + * Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=21-120385 + */ @Composable fun RoomDetailsView( state: RoomDetailsState, From 7a05fc118a602fd2bac3b5522cae492a5ab1c2de Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 1 Jun 2026 18:15:02 +0200 Subject: [PATCH 299/471] Fix test --- .../android/features/roomdetails/impl/RoomDetailsViewTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 4e0640953b..7c5b8e4ac8 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -334,6 +334,7 @@ class RoomDetailsViewTest { setRoomDetailView( state = aRoomDetailsState( eventSink = EventsRecorder(expectEvents = false), + roomType = RoomDetailsType.Dm(aDmRoomMember(userId = A_USER_ID)), dmOtherMemberDetailsState = aUserProfileState(userId = A_USER_ID), ), onProfileClick = callback, From b016fb3dc234d4a3ae9c278776397739c0665b18 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 1 Jun 2026 16:36:13 +0000 Subject: [PATCH 300/471] Update screenshots --- .../images/features.roomdetails.impl_RoomDetailsA11y_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_0_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_10_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_11_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_12_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_13_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_14_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_15_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_16_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_17_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_18_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_19_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_1_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_20_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_21_en.png | 4 ++-- .../features.roomdetails.impl_RoomDetailsDark_22_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_2_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_3_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_4_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_5_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_6_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_7_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_8_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetailsDark_9_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_0_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_10_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_11_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_12_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_13_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_14_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_15_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_16_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_17_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_18_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_19_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_1_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_20_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_21_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_22_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_2_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_3_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_4_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_5_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_6_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_7_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_8_en.png | 4 ++-- .../images/features.roomdetails.impl_RoomDetails_9_en.png | 4 ++-- 47 files changed, 94 insertions(+), 94 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png index 25302db21d..2ad3818d42 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7c187e173ae6d02d6c95c03bcc24cce91b0922e6aa5a08677732748aab0fc30 -size 85320 +oid sha256:9dc6120d4320dafa66be77e8386a4b636b6b8a77e30ac10d9d6f31932839036a +size 67337 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png index 739ff5f268..d45df5d881 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8979ec6209ad6e0deb6946e00ce227f3db41a565218afe31b72b889bee224daa -size 46268 +oid sha256:6a2dc4ca92e9ab49a29158ef001408ae1ca5d7f8cfed30876c223a4bea31624d +size 40091 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png index 7609f5e725..951db894ff 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16200632461dcbbf0f6211b7064b61a1e2fa04cd34e14946beb12b9e9f4d7b7f -size 43901 +oid sha256:4c31008e2eff545cee39d267475c1f346fe96c6c3600572d812a903659f79604 +size 38262 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png index ef1954720c..e57d2d4d66 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2279893a284e620cf487f6370982a14278fd57bb278e53932a5fd7c5cab8e95e -size 43033 +oid sha256:a5a22703e5a398443ff2457f5705177648f2ce5172c91294e10860ff16ceb25a +size 38258 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png index a0cee002e6..a7b98bd9a5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d4da60a94c56b13b65efb797825c54c1e107fe4440f56b1fcac5d47779cbf64 -size 44502 +oid sha256:e121018b1e0344c0f07453fa63c99ec382797f82b6828c0291c9724a4e8bdcc9 +size 38601 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png index eb330780d8..3db8f3bf06 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee2899f7870b09f287b45b327d5d0462b423ba65afa1087dec1be862507378de -size 44411 +oid sha256:bd3f8738d58814fa420f683c7bd80a8f805195b15e4973bed1d33b5aa3062533 +size 38535 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png index e09c2dfd95..2277a942a3 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78ba2ddfdfc47febc0503daa0985a5fdf04ef15bdc391b4a88c3c630b1946b35 -size 46004 +oid sha256:00e744b9f2652c2cd4cc05f798786d2a090843483d616e1f4868e0431f5495d8 +size 39947 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png index cc49f40494..e036045426 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a832b0e395498b15e176529ea55c50ed129f6d2330fbec4ff1f8e3dcaeab93f -size 46543 +oid sha256:b7832f9cca5732d13dc8d2f282df245c5b09e908c84b37104f3f9764fb2ca189 +size 40271 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png index e8c0ec1046..47de1311bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8e738bd873339d195b4190f65a18016f2f0eb1bbc9f513a2b25467cb5f5828c -size 44745 +oid sha256:fc2f69b95a34c455534f04a21f9edde12acfbdd4c57c190ad7aa22352c2206ec +size 38838 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png index 5098ecb3a2..25d803c185 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:892dd301ab08c6a302e8286d3f86878178130a22baf550402f2102ba98f0d21c -size 43982 +oid sha256:096bfadc7faa370132e03822cb9c66eacdc2b7fa07a65ce501fe1fec7c0d8a81 +size 38236 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png index 899ef403a4..d8355a82b6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:809c597757d6f1385803c1378a494f24309b533168df59c7e06852c1ce34b8d3 -size 41408 +oid sha256:2710ef0a99a60eca8b369a6c2b0d45658f7d1b2e44900137a625ade9891986bd +size 37199 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png index 0625fcfe05..7f46bba7b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3756793e5d92f0fac18e96a49e69ac7ed6901c1195d3e6526088027405e82dbf -size 41361 +oid sha256:3e51350f6799f4b5ed3a0dadfa226d839e4be8e7182bad82ba168381c5a7f7b7 +size 37223 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png index 346c9cf052..58108d410a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:099f571485ce8476d4b8a2313610b6c1d03a7d7420132e06d6258b483d640a08 -size 39130 +oid sha256:922caafea1f17073dfe1352762f194694d4dec8facc93cfa901be111fc99832d +size 34856 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png index a58193c18f..135a83f7ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66fd2cfb9f343c77b8db0a4fbc0d7a1571a4dfe1d70c78823a4ba03bfbc846eb -size 44934 +oid sha256:7764a75469a6e2f585c2b919ebad99e61a627d1adef05a6de85370eeb08a0df1 +size 38934 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png index 37bb88b0fb..e872ff70af 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:465ecd337f705262c8f533b3b01daa6a548c6ee88752be405609660ddfaf56b1 -size 44676 +oid sha256:fd3546dd3a61245c611fbd94ba510ba96ea0b3c63048aa6537a91479a45542a4 +size 38696 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png index 6356cec349..85cd90d544 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cca233c171b6c6709a94163ca1f0024a42ff66200383fdbcaf57b321251522e -size 44390 +oid sha256:bbbfc332a093a408354d75064d960f20ca761f606741349723c46d8ab68b6f59 +size 38585 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png index 3846aec585..21798e9560 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e5e8b39197d115c6190b89fd54033b3319b36dfbf5c6f152dd84e6c79147bbb -size 37877 +oid sha256:7ab49c4c721c0b192786afb47c47fe9d205e10734e6c20410a522b0a33624b47 +size 32001 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png index 08ca4ad1e6..755dc1c30b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4899d2ffcc6da1f414f7fb223440b8463250dc5ac07558cfaee2947f95c93362 -size 42236 +oid sha256:1dde565f1a8a2b58c1eb9446b04e4c954dbb6e1c2d2fa843acac9e7ae850bd31 +size 30785 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png index 062c440463..f095b87dd2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:577381a33e7f0a88ceebb7f2be6528c3e99417cf1351b74c134aab948664cc64 -size 42149 +oid sha256:bd58ff9f30ac4aadf4c7db8d26f0127e8182f6806febd54bbc2a69a071b9606b +size 37445 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png index 1ff34bd6e4..29a2163b26 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2bc2699d8dcf770103726c571a6966eacf173cbbad05c4fa6188815aa9ae231 -size 41052 +oid sha256:3cfc733deef5472fc65f0de1e6318e6bac781be6a399c9b1441fd1e2e4123c26 +size 36925 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png index c1decbd918..91cdc6312f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cba49398954a404def6b37f3cde057cb88d6f59249b943f1689403ccd902326 -size 42024 +oid sha256:0e3d91ed010211048d9d1e7d659d5159ec7c771af53672cf61825ce8a719c6fc +size 31495 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png index 379f61db22..c6781a1908 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:405a29c158295616560152c5790f189a8019bd635a53a69b581c8aa595e6e9ea -size 45494 +oid sha256:167eb16cc4197529054fd0522a73db2b36b4369f149bce46c65a7d2deb449559 +size 39193 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png index d0ffc70f42..68a1b92944 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f948e9cc2eb7bc73b0441fbdea72d582a042895b3404c8302b7e182c3481fae -size 44425 +oid sha256:b49f74cb3db16cb8f4c8c0cfadf08f64a29b147598e420e264bfd9121b485d35 +size 38615 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png index 2894ac3c71..915d514b07 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4702b18ad5d203050140e6e1a18553344f52eb5482fcaf6ab497698b9ec59fd -size 44432 +oid sha256:50463dcf8b496677bb8d6c8ac1af92378aca52fde19a421e227337425a22457c +size 38765 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png index afabfcecf5..224efc8973 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4189b58dd1263a81566ea86d9881c02f053ecb2a91ba9442553fbe0f230e6860 -size 47105 +oid sha256:3cedec6cfed8804e7ef5814255da7b690051a7f29178094aa558dd151bebec5f +size 41410 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png index 9f07ad1624..085716f2b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fced9019f81e5804ea57c0b1a2750868626ad35b9a7227009a796d289595556 -size 44659 +oid sha256:578c3e724112d29557432e08bc442fe127163dc2c33ec22560e7e9768b14bc79 +size 39456 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png index 74c4a9eef1..252f8ae7da 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be9bb839a503d169d67d4332a133ce2b3d76b3ff4c52c063436521f8a74c973e -size 43894 +oid sha256:74daf2e85aaee7e01bfd50446e25d1cfe057ad95adfda6efcc8fd74ee4894cb7 +size 39318 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png index bd85882fde..e7045c0261 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_12_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82c2781c37b7cefa198f2061a2c8e900b27bd57653db88733be5ff55dfaf5eee -size 45251 +oid sha256:4c3484c638961188614a9b70c8d9c5fb20c8c5da04fa7907274bc809ee305b2e +size 39828 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png index cac488f3b8..931dac5c8e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36feab599a6a2d4269b68f42149a6f82832ac0f6f0e9f01fb49adc21b3a58d93 -size 45172 +oid sha256:5add6a8689ea768e86cd035958f6d06f71bf882a8059e3addaa4efef25e77f91 +size 39779 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png index 3e7cfcf672..87c688b249 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_14_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04bedd62b5ceba766197dd18da9efb0ff086c2c01c9c19446f8a3c42ed43ad87 -size 46813 +oid sha256:0c9bf7f1707d5304bd9df44f086ae966ee7ce6aeefd1f3f830ca9ad5aff9e6e6 +size 41261 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png index 27ec9f7a25..6d461a206e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:828b065aaeeccd826e98e9601069819d7a75ffabab9820a834cacb627416edc0 -size 47409 +oid sha256:5d7cc787ca66a1d32df5af87ce2b5c17837a8c650622c8a87b133fe63bbdcecf +size 41662 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png index f96e1aec74..9314d95855 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4740bcd118f8583334dd1c03b5011e33c5384ea6810d1c83711f538cf9f1d165 -size 45521 +oid sha256:522ac292a26b23b260305de8e7ad03b9603c2fa7490c2f420d226c2821914a18 +size 40062 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png index 4d88149a93..10007bb5c6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fab53a63021ddae8384aed8eaf0a3803d06e8c80645ecc13f79a7f5cced6e68 -size 45024 +oid sha256:c4d7cc1bc96f2271efd707d2f8e5a8cc0e19a2eab8981bb1e27e822e54cae904 +size 39615 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png index fa7e99aef2..3a1a686dde 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89a2c619412ad9ca43c79cb76ebe72b827da71743dda7044a9b44de2990f160b -size 42388 +oid sha256:e24cb3232d0c59c8ce020def10be0210860de81be50314706fda1c37f928ce51 +size 38529 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png index 9b27b61083..c2e89cf6b5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_19_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56ceed440bb51039ffe7e4419477a57a235eed153a0188838e6265c241896a82 -size 42262 +oid sha256:22d74ec9cc245f83fdb71fcb67bff0f4ca061ddd410f64fde61a12ccbb2ffa68 +size 38492 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png index 0542085126..55b742c30c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b03e9da2d73cc0e3d0485b73470e576d7ebd4b8b5f375f2a478f1c1524772fa -size 39948 +oid sha256:1d91aed4496b28952522a084145819947c0eeb704bfafab638f12fa11876acda +size 36068 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png index a320657a26..ca7d80865a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_20_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a686a2596b9b85b17c6882aa986b12e13a83a47ed217c4eb6de6aa2cb3ec2d50 -size 45717 +oid sha256:42a2011218c00319b7511ced2bc0187d5aeebb874e236a71a6fcd59ea2190feb +size 40240 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png index 920b670806..50b6f8a28d 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_21_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6bda369c1754e2c07216f4bff0285e017f87b550fe0dc6bb5f127845d52c9b6 -size 45462 +oid sha256:d87aab87e3d86ede030054a68e905cf793046ff7443233a1418e12b067d8021b +size 39942 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png index aeaa04b269..68d6690ed4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_22_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:285045769d912e98df0371c829affd925642c8a20cd0b6b09dfa92ab5f143538 -size 45131 +oid sha256:8f3719a8515a3e9edeaa0db8e1d02eccb5c4330a7767d99f25051d4dce46f7b2 +size 39812 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png index e7fe0e7cef..de4aaa99d4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:463548e170b48509d68c0993acb000748b19921e5d4be784b07ce83252c8a5bb -size 38613 +oid sha256:688c59277fd440e8b96c83857084be78a23218151845ef5483175b0b2e322401 +size 33306 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png index 76eb564d8c..33c465e48f 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1d19c2b8c6b3c80b68214da2bb9f54a6897a279aa70e2d8cab88c291332cece -size 42980 +oid sha256:1c424390f8c4930db36d474806290c525edfd4b2e25689c95516fe74e5bdc77b +size 31484 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png index 40ab9e310a..534bdb2100 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2057479cad5d3b9a89ec835ee1d06705c6c459f7810c2875bea26b975944bcf7 -size 42960 +oid sha256:e76c8967b4cd416dc4a09e94290dba727e1ca6de51186afd7993ed7561ad49e4 +size 38593 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png index e549a92821..99fdc036ca 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:073addf26e8d0b638bce93db99d1fe9740abd89613a1eb8ef5bb4324e489d6fa -size 41924 +oid sha256:2edc249c3352db53a478c033ca5ebee16a3bfde7c9110b893315b739792f2409 +size 38215 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png index 29a8b0b4b1..86b249d17c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56d72cab4a9d7af568cc497b68d6ca9055874544cfd462c93da37fe368801fdf -size 42984 +oid sha256:9f8a90a86b7dca19bd6964a2a78c610308915d4c55327601499b03f58b104236 +size 32498 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png index 91cf700f52..2f07c45fd6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9141f5fa4e054dc5cf70126309830bc38d564d467a215eaa1130f3601b15e761 -size 46350 +oid sha256:aa079faa553ee5f223cd96c50c89dcd207b226b108da92287f1f70fb83078ee6 +size 40503 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png index fe0b629925..530f089571 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a062b8cbe4dfbd94a5b068b1f7c4654ed8b697a7e98ae4c2bcb6c3e5bf473b49 -size 45289 +oid sha256:d98ec0320a6360b56c01ddd193da24c397ca95b0127b31f56375e0fc970264ac +size 39913 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png index 26d59539db..93915f1e2e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46aafd39e1a93f8d357def4c5f3f024ad0ee13931ebdb9f72475dbc911fd7532 -size 45266 +oid sha256:037424649684d6559b0a62d7ef1001c63b5a7547bff8398aaa27cc37002d1491 +size 39939 From 97135272ec5832f6e8a8d9b6ecda19472ec15524 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jun 2026 10:22:34 +0200 Subject: [PATCH 301/471] [Media viewer] Improve rendering of files Closes #6899 Closes #6908 --- .../libraries/mediaviewer/api/MediaInfo.kt | 24 ++++++ .../impl/local/DefaultLocalMediaRenderer.kt | 1 + .../mediaviewer/impl/local/LocalMediaView.kt | 3 +- .../impl/local/file/MediaFileView.kt | 74 ++++++++++--------- .../impl/local/file/MediaInfoFileProvider.kt | 8 +- .../impl/viewer/MediaViewerView.kt | 5 ++ 6 files changed, 78 insertions(+), 37 deletions(-) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt index fedf376a60..adfffd8af4 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt @@ -103,6 +103,30 @@ fun aPdfMediaInfo( duration = null, ) +fun aZipMediaInfo( + filename: String = "a zip file.zip", + caption: String? = null, + formattedCaption: CharSequence? = null, + senderName: String? = null, + dateSent: String? = null, + dateSentFull: String? = null, +): MediaInfo = MediaInfo( + filename = filename, + fileSize = 12 * 1024 * 1024, + caption = caption, + formattedCaption = formattedCaption, + mimeType = MimeTypes.Pdf, + formattedFileSize = "45MB", + fileExtension = "zip", + senderId = UserId("@alice:server.org"), + senderName = senderName, + senderAvatar = null, + dateSent = dateSent, + dateSentFull = dateSentFull, + waveform = null, + duration = null, +) + fun anApkMediaInfo( senderId: UserId? = UserId("@alice:server.org"), senderName: String? = null, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt index 96450d99bf..319aebe33e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt @@ -41,6 +41,7 @@ class DefaultLocalMediaRenderer( textFileViewer = textFileViewer, audioFocus = audioFocus, onClick = {}, + onOpenWith = null, ) } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt index cc276b52c8..c19b1a24c0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt @@ -31,6 +31,7 @@ fun LocalMediaView( bottomPaddingInPixels: Int, audioFocus: AudioFocus?, onClick: () -> Unit, + onOpenWith: (() -> Unit)?, textFileViewer: TextFileViewer, modifier: Modifier = Modifier, isDisplayed: Boolean = true, @@ -80,7 +81,7 @@ fun LocalMediaView( uri = localMedia?.uri, info = mediaInfo, modifier = modifier, - onClick = onClick, + onOpenWith = onOpenWith, ) } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt index 912ea88e99..a1c57ef272 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaFileView.kt @@ -9,23 +9,16 @@ package io.element.android.libraries.mediaviewer.impl.local.file import android.net.Uri -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.rotate +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 @@ -33,55 +26,54 @@ 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.core.bool.orFalse +import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio +import io.element.android.libraries.designsystem.components.BigIcon 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.ButtonSize +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.helper.formatFileExtensionAndSize +import io.element.android.libraries.mediaviewer.impl.R import io.element.android.libraries.mediaviewer.impl.local.LocalMediaViewState import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState +import io.element.android.libraries.ui.strings.CommonStrings +/** + * Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=3361-16623 + */ @Composable fun MediaFileView( localMediaViewState: LocalMediaViewState, uri: Uri?, info: MediaInfo?, - onClick: () -> Unit, + onOpenWith: (() -> Unit)?, modifier: Modifier = Modifier, ) { val isAudio = info?.mimeType.isMimeTypeAudio().orFalse() localMediaViewState.isReady = uri != null - - val interactionSource = remember { MutableInteractionSource() } Box( modifier = modifier - .padding(horizontal = 8.dp) - .clickable( - onClick = onClick, - interactionSource = interactionSource, - indication = null - ), + .padding(horizontal = 8.dp), contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Box( - modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .background(ElementTheme.colors.iconPrimary), - contentAlignment = Alignment.Center, - ) { - Icon( - imageVector = if (isAudio) CompoundIcons.Audio() else CompoundIcons.Attachment(), - contentDescription = null, - tint = ElementTheme.colors.iconOnSolidPrimary, - modifier = Modifier - .size(32.dp) - .rotate(if (isAudio) 0f else -45f), - ) + val isApk = info?.mimeType == MimeTypes.Apk + val icon = when { + isAudio -> CompoundIcons.Audio() + isApk -> CompoundIcons.Bug() + else -> CompoundIcons.Files() } + BigIcon( + modifier = Modifier.align(Alignment.CenterHorizontally), + style = BigIcon.Style.Default( + vectorIcon = icon, + usePrimaryTint = true, + ), + ) if (info != null) { Spacer(modifier = Modifier.height(20.dp)) Text( @@ -100,6 +92,20 @@ fun MediaFileView( overflow = TextOverflow.Ellipsis, color = ElementTheme.colors.textPrimary ) + if (onOpenWith != null) { + val (icon, textResId) = if (isApk) { + IconSource.Resource(R.drawable.ic_apk_install) to CommonStrings.common_install_apk_android + } else { + IconSource.Vector(CompoundIcons.PopOut()) to CommonStrings.action_open_with + } + OutlinedButton( + modifier = Modifier.padding(top = 24.dp), + size = ButtonSize.Small, + leadingIcon = icon, + onClick = onOpenWith, + text = stringResource(id = textResId), + ) + } } } } @@ -115,6 +121,6 @@ internal fun MediaFileViewPreview( localMediaViewState = rememberLocalMediaViewState(), uri = null, info = info, - onClick = {}, + onOpenWith = {}, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt index 8e41a4809a..3214236253 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/file/MediaInfoFileProvider.kt @@ -10,11 +10,15 @@ package io.element.android.libraries.mediaviewer.impl.local.file import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.mediaviewer.api.MediaInfo -import io.element.android.libraries.mediaviewer.api.aPdfMediaInfo +import io.element.android.libraries.mediaviewer.api.aZipMediaInfo +import io.element.android.libraries.mediaviewer.api.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo open class MediaInfoFileProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aPdfMediaInfo(), + aZipMediaInfo(), + anAudioMediaInfo(), + anApkMediaInfo(), ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 3a7c006593..c56b6b64fe 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -239,6 +239,9 @@ fun MediaViewerView( onRetry = { state.eventSink(MediaViewerEvent.LoadMedia(dataForPage)) }, + onOpenWith = { + state.eventSink(MediaViewerEvent.OpenWith(dataForPage)) + }, onDismissError = { state.eventSink(MediaViewerEvent.ClearLoadingError(dataForPage)) }, @@ -333,6 +336,7 @@ private fun MediaViewerPage( isUserSelected: Boolean, onDismiss: () -> Unit, onRetry: () -> Unit, + onOpenWith: () -> Unit, onDismissError: () -> Unit, onShowOverlayChange: (Boolean) -> Unit, audioFocus: AudioFocus?, @@ -387,6 +391,7 @@ private fun MediaViewerPage( currentOnShowOverlayChange(!currentShowOverlay) } }, + onOpenWith = onOpenWith, isUserSelected = isUserSelected, audioFocus = audioFocus, ) From 1713555a67984fb64c939f714d8f5cd687381320 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 2 Jun 2026 10:29:23 +0200 Subject: [PATCH 302/471] Introduce PlatformLocationProvider to replace the maplibre one. --- .../impl/common/PlatformLocationProvider.kt | 130 ++++++++++++++++++ .../location/impl/common/UserLocationState.kt | 3 +- .../service/LiveLocationSharingService.kt | 4 +- 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt new file mode 100644 index 0000000000..dbc4ac9040 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common + +import android.Manifest +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.PackageManager +import android.location.LocationManager +import android.os.Handler +import android.os.HandlerThread +import androidx.annotation.RequiresPermission +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.core.location.LocationListenerCompat +import androidx.core.location.LocationManagerCompat +import androidx.core.location.LocationRequestCompat +import androidx.core.os.ExecutorCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.stateIn +import org.maplibre.compose.location.DesiredAccuracy +import org.maplibre.compose.location.Location +import org.maplibre.compose.location.LocationProvider +import org.maplibre.compose.location.PermissionException +import org.maplibre.compose.location.asMapLibreLocation +import org.maplibre.spatialk.units.Length +import org.maplibre.spatialk.units.extensions.inMeters +import kotlin.time.Duration + +@SuppressLint("InlinedApi") +class PlatformLocationProvider( + context: Context, + private val updateInterval: Duration, + private val minDistance: Length, + private val desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High, + coroutineScope: CoroutineScope, + sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000), +) : LocationProvider { + override val location: StateFlow + + init { + if (!handlerThread.isAlive) handlerThread.start() + if ( + context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && + context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED + ) { + throw PermissionException() + } + + val locationManager = context.getSystemService(LocationManager::class.java) + val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) } + val locationFlow = if (provider != null) { + createProviderFlow(locationManager, provider) + } else { + emptyFlow() + } + location = locationFlow.stateIn(coroutineScope, sharingStarted, null) + } + + @RequiresPermission( + anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION] + ) + private fun createProviderFlow(locationManager: LocationManager, provider: String) = callbackFlow { + send(locationManager.getLastKnownLocation(provider)?.asMapLibreLocation()) + val listener = LocationListenerCompat { trySend(it.asMapLibreLocation()) } + val request = LocationRequestCompat.Builder(updateInterval.inWholeMilliseconds) + .setQuality(desiredAccuracy.toLocationRequestQuality()) + .setMinUpdateDistanceMeters(minDistance.inMeters.toFloat()) + .build() + LocationManagerCompat.requestLocationUpdates( + locationManager, + provider, + request, + ExecutorCompat.create(Handler(handlerThread.looper)), + listener, + ) + awaitClose { LocationManagerCompat.removeUpdates(locationManager, listener) } + } + + private companion object { + private val PROVIDERS_BY_PRIORITY = listOf( + LocationManager.FUSED_PROVIDER, + LocationManager.GPS_PROVIDER, + LocationManager.NETWORK_PROVIDER, + ) + private val handlerThread by lazy { HandlerThread("PlatformLocationProvider") } + } +} + +private fun DesiredAccuracy.toLocationRequestQuality(): Int = when (this) { + DesiredAccuracy.Highest, DesiredAccuracy.High -> LocationRequestCompat.QUALITY_HIGH_ACCURACY + DesiredAccuracy.Balanced -> LocationRequestCompat.QUALITY_BALANCED_POWER_ACCURACY + DesiredAccuracy.Low, DesiredAccuracy.Lowest -> LocationRequestCompat.QUALITY_LOW_POWER +} + +@Composable +@RequiresPermission( + anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION] +) +fun rememberPlatformLocationProvider( + updateInterval: Duration, + minDistance: Length, + desiredAccuracy: DesiredAccuracy = DesiredAccuracy.High, + context: Context = LocalContext.current, + coroutineScope: CoroutineScope = rememberCoroutineScope(), + sharingStarted: SharingStarted = SharingStarted.WhileSubscribed(stopTimeoutMillis = 1000), +): PlatformLocationProvider { + return remember(context, updateInterval, minDistance, desiredAccuracy, coroutineScope, sharingStarted) { + PlatformLocationProvider( + context = context, + updateInterval = updateInterval, + minDistance = minDistance, + desiredAccuracy = desiredAccuracy, + coroutineScope = coroutineScope, + sharingStarted = sharingStarted, + ) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt index 632ddef38d..01533a421d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalInspectionMode import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.compose.location.Location -import org.maplibre.compose.location.rememberAndroidLocationProvider import org.maplibre.compose.location.rememberNullLocationProvider import org.maplibre.spatialk.units.extensions.meters import kotlin.time.Duration.Companion.seconds @@ -32,7 +31,7 @@ fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState val locationProvider = if (isPreview || !hasLocationPermission) { rememberNullLocationProvider() } else { - rememberAndroidLocationProvider( + rememberPlatformLocationProvider( updateInterval = 5.seconds, desiredAccuracy = DesiredAccuracy.High, minDistance = 5.meters, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt index a926f1255b..b5b8342b96 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -14,6 +14,7 @@ import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION import android.os.IBinder import androidx.core.app.ServiceCompat import dev.zacsweers.metro.Inject +import io.element.android.features.location.impl.common.PlatformLocationProvider import io.element.android.features.location.impl.di.LocationBindings import io.element.android.features.location.impl.live.notification.LiveLocationSharingNotificationCreator import io.element.android.libraries.architecture.bindings @@ -34,7 +35,6 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import org.maplibre.compose.location.AndroidLocationProvider import org.maplibre.compose.location.DesiredAccuracy import org.maplibre.spatialk.units.extensions.inMeters import org.maplibre.spatialk.units.extensions.meters @@ -90,7 +90,7 @@ class LiveLocationSharingService : Service() { Timber.d("LiveLocationSharingService listening to location updates") appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow() .flatMapLatest { minDistanceMeters -> - val locationProvider = AndroidLocationProvider( + val locationProvider = PlatformLocationProvider( context = applicationContext, updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds, minDistance = minDistanceMeters.meters, From a9bf48779b7b9385bdf51ecfd3fc2e4f4adfe2bf Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 2 Jun 2026 08:43:54 +0000 Subject: [PATCH 303/471] Update screenshots --- ...ies.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png | 4 ++-- ...ies.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png | 3 +++ ...ies.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png | 3 +++ ...s.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png | 4 ++-- ...s.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png | 3 +++ ...s.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png | 3 +++ ....mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png | 4 ++-- 10 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png index a094305aad..0ee71c18e5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f520fc8eac5e100bc4e8b0bf0c84e615078dabbdf502411e6dca5a4d579cfcab -size 12255 +oid sha256:4decbb84ca2cab1260c41fdeec92fe41671cf09b666657ef6c26469b5b18856a +size 12942 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png new file mode 100644 index 0000000000..1523f18525 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dac26ebe7db7c63c3f3c443d80a5eb7c33bc7171872c605d694265113931bfca +size 14305 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png new file mode 100644 index 0000000000..f2e777c40d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ce76770f233a9098af885c172147f15acb520c3732cc6ab476e5e0e54fcd0b8 +size 14515 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png index 3692cfba78..03a0d65856 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79fd6fba99568650dcbdac47a0ecfda6faa2cd893ded73d5ac4cbf852b59bd63 -size 11889 +oid sha256:1290c1fb2023cb95f54b6c8d31b044b52d78d7159bf641080cebc6be77588ce7 +size 12482 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png new file mode 100644 index 0000000000..a9818dae5a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bef8e1712b4a6e52e9289e9670dcefa437db08466d74a46c90c5025effa0a83 +size 13867 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png new file mode 100644 index 0000000000..81bb4deb1b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd9fefe7c4c46dfc419576f04470bd0b36f0ce3fab6d7fbb4d6c676341ba4cb8 +size 14071 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png index a092bf37ac..6a08be241c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e23aad00065523bf1ee0e26cd094eb6a9bdd73d65f0bd9cac62cb79e7876481 -size 196533 +oid sha256:311f06e3b866362380c570d7831b69d72b98de07c3d6c0a1b84abb260362084a +size 197521 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png index 619bd4e8c6..04bf989a7e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d98ceaeb669342160d04783f07fbb21929d8be46d30181b60e0f09a99abfe79 -size 197127 +oid sha256:50860f113539dbd61adec9b60ebe0bf43e7c6775c6285caa875987fc0b44e168 +size 198108 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png index 31403f8980..5d8a4a590a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9cd21b6d9a9d0a9ef656bf895d6d135ba76160695355ce034fe985991d6955b -size 123949 +oid sha256:77eda575306e00e2b6fc5b434a3f5c276bf3689266b043fd3b1563784d28e628 +size 125448 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png index bc0720e4b4..5bd06e6364 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2dce0794e50116043fde9f6eb76b0c05ca21fc9cee26bde01618024c24c154b2 -size 14952 +oid sha256:bc51776a66a9289423196ae9cb39cd57ba718fab2c795631ff1d9e7517cdf763 +size 16580 From 0a1ef5d121e9c0ea13e3b80ffa21d6732f9b4cac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jun 2026 10:43:15 +0200 Subject: [PATCH 304/471] [Media viewer] Increase topAppBarHeight so that text and PDF files are not rendered below the top app bar. --- .../libraries/mediaviewer/impl/viewer/MediaViewerView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index c56b6b64fe..2d72875ecb 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -106,7 +106,7 @@ import me.saket.telephoto.zoomable.OverzoomEffect import me.saket.telephoto.zoomable.ZoomSpec import me.saket.telephoto.zoomable.rememberZoomableState -val topAppBarHeight = 88.dp +val topAppBarHeight = 112.dp /** * Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=3361-16623 From bdf32eac97a4e5cc029125879d118a98322466c3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 2 Jun 2026 09:36:10 +0000 Subject: [PATCH 305/471] Update screenshots --- ...ediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png | 4 ++-- ...iaviewer.impl.local.txt_TextFileContentView_Night_2_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png index 511027b648..99b5263e02 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94b71c47da614e247f1c9bbe6df50ae036c5587b964f4df460859927d4807d37 -size 6154 +oid sha256:db3ad4fbd7c140588ed40887455e3ee6b77968ca8682769826f0a447d867ac2f +size 6115 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png index cf25daf7f0..2c843d2676 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5d66ac02ac2d39125c26406f70880f2b58f3ffb88569f19b0e5104a03293034 -size 6097 +oid sha256:8b91198bf6f1cc53e13eea6c7092142800760c7103349c37ca931d722a549616 +size 6064 From 1a40e460aea4557659470cf97a3eb55cf953c952 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 2 Jun 2026 12:23:14 +0200 Subject: [PATCH 306/471] Handle unrecoverable location errors and stop sharing --- .../impl/common/PlatformLocationProvider.kt | 1 - .../DefaultActiveLiveLocationShareManager.kt | 5 ++++ .../impl/live/service/LiveLocationReceiver.kt | 3 +- .../service/LiveLocationSharingCoordinator.kt | 11 ++++++++ .../service/LiveLocationSharingService.kt | 28 ++++++++++++------- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt index dbc4ac9040..7988c73e68 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt @@ -58,7 +58,6 @@ class PlatformLocationProvider( ) { throw PermissionException() } - val locationManager = context.getSystemService(LocationManager::class.java) val provider = PROVIDERS_BY_PRIORITY.firstOrNull { LocationManagerCompat.hasProvider(locationManager, it) } val locationFlow = if (provider != null) { diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt index fd16bea515..c24d3f9cc2 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/DefaultActiveLiveLocationShareManager.kt @@ -133,6 +133,11 @@ class DefaultActiveLiveLocationShareManager( } } + override suspend fun onUnrecoverableError() { + Timber.d("ActiveLiveLocationShareManager unrecoverable error, stopping all shares") + localSharingRoomIds.value.toList().forEach { stopShare(it) } + } + override suspend fun onLocationUpdate(location: Location) { val activeSharesCount = localSharingRoomIds.value.size Timber.d("ActiveLiveLocationShareManager received location update for $activeSharesCount active share(s)") diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt index adba75730c..90311e7d4a 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.impl.live.service import io.element.android.features.location.api.Location -fun interface LiveLocationReceiver { +interface LiveLocationReceiver { suspend fun onLocationUpdate(location: Location) + suspend fun onUnrecoverableError() {} } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt index e39acb14e8..63618baa3c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingCoordinator.kt @@ -77,6 +77,17 @@ class LiveLocationSharingCoordinator internal constructor( } } + suspend fun dispatchUnrecoverableError() { + Timber.d("LiveLocationSharingCoordinator dispatching unrecoverable error") + receivers.forEach { (sessionId, receiver) -> + runCatchingExceptions { + receiver.onUnrecoverableError() + }.onFailure { + Timber.e(it, "Failed to dispatch unrecoverable error for session $sessionId") + } + } + } + suspend fun dispatch(location: Location) { val currentTimeMillis = nowMillis() val millisSincePrevious = currentTimeMillis - lastDispatchMillis.load() diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt index b5b8342b96..d682ba0df6 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -30,12 +30,15 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.maplibre.compose.location.DesiredAccuracy +import org.maplibre.compose.location.PermissionException import org.maplibre.spatialk.units.extensions.inMeters import org.maplibre.spatialk.units.extensions.meters import timber.log.Timber @@ -48,7 +51,6 @@ class LiveLocationSharingService : Service() { @Inject lateinit var coordinator: LiveLocationSharingCoordinator @Inject lateinit var notificationCreator: LiveLocationSharingNotificationCreator @Inject lateinit var appPreferencesStore: AppPreferencesStore - @Inject lateinit var appForegroundStateService: AppForegroundStateService @AppCoroutineScope @@ -62,8 +64,8 @@ class LiveLocationSharingService : Service() { override fun onCreate() { super.onCreate() Timber.d("LiveLocationSharingService onCreate") + bindings().inject(this) runCatchingExceptions { - bindings().inject(this) appForegroundStateService.updateIsSharingLiveLocation(true) coroutineScope = appCoroutineScope.childScope(Dispatchers.Default, "LiveLocationSharingService") val notificationId = NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.LIVE_LOCATION) @@ -81,6 +83,7 @@ class LiveLocationSharingService : Service() { startLocationUpdatesListener() }.onFailure { Timber.e(it, "Failed to start live location sharing service") + appCoroutineScope.launch { coordinator.dispatchUnrecoverableError() } stopSelf() } } @@ -90,14 +93,19 @@ class LiveLocationSharingService : Service() { Timber.d("LiveLocationSharingService listening to location updates") appPreferencesStore.getLiveLocationMinimumDistanceInMetersUpdateFlow() .flatMapLatest { minDistanceMeters -> - val locationProvider = PlatformLocationProvider( - context = applicationContext, - updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds, - minDistance = minDistanceMeters.meters, - desiredAccuracy = DesiredAccuracy.Balanced, - coroutineScope = coroutineScope - ) - locationProvider.location + try { + PlatformLocationProvider( + context = applicationContext, + updateInterval = UPDATE_INTERVAL_IN_SECOND.seconds, + minDistance = minDistanceMeters.meters, + desiredAccuracy = DesiredAccuracy.Balanced, + coroutineScope = coroutineScope + ).location + } catch (exception: PermissionException) { + Timber.e(exception, "Failed to create PlatformLocationProvider") + coordinator.dispatchUnrecoverableError() + emptyFlow() + } } .filterNotNull() .map { location -> From 1c058037beec40912d82c42d534f56c97ee4b15f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jun 2026 12:50:51 +0200 Subject: [PATCH 307/471] Update the way we display file format and size. --- .../mediaviewer/api/helper/FileExtensionAndSizeFormatter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/FileExtensionAndSizeFormatter.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/FileExtensionAndSizeFormatter.kt index fc40bca8dd..e546f50565 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/FileExtensionAndSizeFormatter.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/helper/FileExtensionAndSizeFormatter.kt @@ -12,8 +12,8 @@ fun formatFileExtensionAndSize(extension: String, size: String?): String { return buildString { append(extension.uppercase()) if (size != null) { - append(' ') - append("($size)") + append(" • ") + append(size) } } } From ab69c3e1583091a88de40d747556d5dab12d153a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 2 Jun 2026 11:06:59 +0000 Subject: [PATCH 308/471] Update screenshots --- ...sages.impl.pinned.list_PinnedMessagesListView_Day_3_en.png | 4 ++-- ...ges.impl.pinned.list_PinnedMessagesListView_Night_3_en.png | 4 ++-- ...meline.components.event_TimelineItemAudioView_Day_0_en.png | 4 ++-- ...meline.components.event_TimelineItemAudioView_Day_1_en.png | 4 ++-- ...meline.components.event_TimelineItemAudioView_Day_2_en.png | 4 ++-- ...meline.components.event_TimelineItemAudioView_Day_3_en.png | 4 ++-- ...meline.components.event_TimelineItemAudioView_Day_4_en.png | 4 ++-- ...line.components.event_TimelineItemAudioView_Night_0_en.png | 4 ++-- ...line.components.event_TimelineItemAudioView_Night_1_en.png | 4 ++-- ...line.components.event_TimelineItemAudioView_Night_2_en.png | 4 ++-- ...line.components.event_TimelineItemAudioView_Night_3_en.png | 4 ++-- ...line.components.event_TimelineItemAudioView_Night_4_en.png | 4 ++-- ...imeline.components.event_TimelineItemFileView_Day_0_en.png | 4 ++-- ...imeline.components.event_TimelineItemFileView_Day_1_en.png | 4 ++-- ...imeline.components.event_TimelineItemFileView_Day_2_en.png | 4 ++-- ...imeline.components.event_TimelineItemFileView_Day_3_en.png | 4 ++-- ...imeline.components.event_TimelineItemFileView_Day_4_en.png | 4 ++-- ...eline.components.event_TimelineItemFileView_Night_0_en.png | 4 ++-- ...eline.components.event_TimelineItemFileView_Night_1_en.png | 4 ++-- ...eline.components.event_TimelineItemFileView_Night_2_en.png | 4 ++-- ...eline.components.event_TimelineItemFileView_Night_3_en.png | 4 ++-- ...eline.components.event_TimelineItemFileView_Night_4_en.png | 4 ++-- .../features.messages.impl.timeline_TimelineView_Day_4_en.png | 4 ++-- .../features.messages.impl.timeline_TimelineView_Day_5_en.png | 4 ++-- .../features.messages.impl.timeline_TimelineView_Day_6_en.png | 4 ++-- .../features.messages.impl.timeline_TimelineView_Day_7_en.png | 4 ++-- ...eatures.messages.impl.timeline_TimelineView_Night_4_en.png | 4 ++-- ...eatures.messages.impl.timeline_TimelineView_Night_5_en.png | 4 ++-- ...eatures.messages.impl.timeline_TimelineView_Night_6_en.png | 4 ++-- ...eatures.messages.impl.timeline_TimelineView_Night_7_en.png | 4 ++-- ...s.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png | 4 ++-- ...mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png | 4 ++-- ...ies.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png | 4 ++-- ...ies.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png | 4 ++-- ...ies.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png | 4 ++-- ...s.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png | 4 ++-- ...s.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png | 4 ++-- ...s.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png | 4 ++-- ....mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png | 4 ++-- ...libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png | 4 ++-- 46 files changed, 92 insertions(+), 92 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en.png index ac1b698ddd..8a295a919c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70b06a4411aaf9fccb7dbb68878ee4a01b0987fe592a1aef6fe474dd555dbd0b -size 42536 +oid sha256:e28b5fb8181f780a372aa944291ef23349f4acc3460786ee152d8e281e85b866 +size 41993 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en.png index fc55c74f1c..14e06bbe27 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ed9d6c254ac362f84b9be0761f0bbd7aecb14343a131d54da4b2be0ad033f87 -size 41165 +oid sha256:d20f6311164e291ae60542c2316cb4d438058322fca1b0db0980f0579874f9c6 +size 40650 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en.png index c056ddecb1..e76e0db72d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcd8208f3737b5b5949af591d2417d16f2da18609f3770201f2e87577b250b2e -size 8947 +oid sha256:45b2370c78dd907e5bf7c5df18b98df636e1099cd1de5b54923f7beb9bd17ccc +size 8680 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en.png index 775c2483f8..429d9d0355 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d114f04976fbce895cc53c2c57df8af1e968067d4ed451c4a11b00ea008e14e -size 11127 +oid sha256:cbbbc103c2a7b5e8d942e0af113568c28dd62863ba0cd1dfb7a5dc9de56b3760 +size 10863 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en.png index 4854996fc2..c45bb0c16b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36487e20168db4594c519e4a6901b18d0e10604aaf10f07f9aa95d44647bdbd6 -size 20287 +oid sha256:f758005da7c969bef056ccf52d27a099e3bc5e900643e50990be430d640edfce +size 20031 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_3_en.png index a27104d999..faf7f18e65 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3576dad3e0443c638112c329854fc4d8818f2045dd2e17fe3a0ccbe9a9b1394d -size 11048 +oid sha256:c43d85e4658b9fb930ef76258479a876d5cde3b545bc4f4a76d86e4ef2d7a82f +size 10773 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en.png index c2273c12ee..f8f7b243b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b4dde4370b8bd6d2bcd8d3c9a877595661ee1b1296dec0f73b041fb556a3921 -size 21282 +oid sha256:c836c0a00fb5697138349028540d5e1cb4f1fddd2fd50d388cb5a1b55ccaa8f4 +size 21023 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en.png index 7fed1167fe..ee33147a2f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc2da3562566b433c6fc5dba185c13127f86890b26644e41d9b5c88f1d130fb3 -size 8671 +oid sha256:3c5351b6f9f14198aec7bb4bd6353b43bbf9f487e192a5c927791756dc354ad9 +size 8431 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en.png index c74490bfdf..802d8b0445 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0870cf3f791d9a1f5203e4d81c01fa986a03a5fa44589befa9f8163c834637e3 -size 10799 +oid sha256:92feff534e8ae02d574f5bddc927bf642e3199dc4f2d89a584d087058d18fc01 +size 10565 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en.png index e3f7918645..013938783e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2031bd6ed35ec1de5a17d1f85aa52c4cb62278e56f1ec5df48c0afe2ddcade38 -size 19216 +oid sha256:2dee0c45a8848f8771bcb0b60f368ccfef29beb7528c227a1a9f07c71a0457dc +size 18940 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en.png index 6ddee33e8c..f5db366e09 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9cac5229b5129b2bc21cae8ead0e7d19b58922056064f28bf0f59074b4e21e6 -size 10742 +oid sha256:9fa672f2cb7dbf0fff171d507e66d1ed8494cbcd93b48bef1ee1b9b75d6717e7 +size 10499 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en.png index 826b4193f0..2ed11e26e2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b693ccea2549b9140beacbf2ffaa8ce409ae7c7f2f9cea0050e56527c2c03fe -size 20380 +oid sha256:5735813e0f9c40e5d861ba0c35ba73689ff7ee41224829289ce017746323922c +size 20150 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en.png index 357520ad31..6e88f8cb19 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:043671b3e2176b35ab9519be38ed5711f3eee99a8583583c6fde11f58250aa48 -size 8251 +oid sha256:e155260fd46cf1550c823e2b0a51f3f6529acfd131fc73a7e66dcaca24f3449e +size 7934 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en.png index 36d8c49114..65beb3b8f8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd96a7daf5c19176ef997fbc80a75c3eeb19d470519ae1d57027b5f388736b1c -size 10526 +oid sha256:49a670d25e379b2385ae14a814cd4aca9a0bdff00c3d62d31b146139c2d0edbe +size 10210 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en.png index 317cdaa271..33772989e8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bba55beec2451cdd8eb5237e85ccaf37d98ff86435015ce019f8ef2f131fe31c -size 20520 +oid sha256:d633e28eea9674914bb018b113313c2e3aed6463e2de5901243f5432087a3c1f +size 20237 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_3_en.png index adf5f2263f..c65619f05d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:207dd1bfa213f3153cf2096d6e961d4c1048664fb2bbaea546af843bb96769a4 -size 10349 +oid sha256:10b79226a4eb5c67afc1fc8ace8c6fb49429a69b1e1518b622dcfd20bc2820ea +size 10032 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en.png index a2ec73f85d..d68ac1725c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15a00a56cdb8156a78905fffd95dc5196f4267768a60a08416417322ec6c9801 -size 20640 +oid sha256:085310083c4150fb70e48087881e529f17005df69cc01dd53807a06266f4f4bb +size 20317 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en.png index c7503a4391..4441c3db6b 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1b8818e2f1e5f822b9560ec30297460bab89f05d94e36a989aaee4277ae1216 -size 8071 +oid sha256:555333bf67a1f4460c0f0d71393c03914fc67cfe6f4e5161468aec1d95e587e2 +size 7803 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en.png index 6a70eee4fa..2755d2b93d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f3bbd229376c0f2731f93cda00e4580c45971e4ac07bac7b2578d45b48ba079 -size 10220 +oid sha256:36009b81c172786ab1d87834b05550af706b355f8d044d364b1f15e58097c8c7 +size 9945 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en.png index febf7e36c2..5c5063083a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2e3ff1bf09d9e616862c74ef29f07c280bf07af8355eb282e51745e0f6927bb -size 19380 +oid sha256:d40a8265e7bffd24954d6fa3194c8c8c65b600518c23d6089b540adabcb751e5 +size 19141 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en.png index 5e1065d2b2..48242a7b75 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aadd6c4d503ebf0224a941c798363e3302018fcc9e726bc31808ab7c5381504 -size 10130 +oid sha256:4ef1884c4cac3debea52affbab071110a49b40cebfd38b316693085464fcc440 +size 9859 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en.png index a9fcd11f7e..3146070439 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f44732ffe4caa44b3fb62bac8a6da3e15a7917f0918314d0f1407b08c8b72da -size 19820 +oid sha256:983620627af06dc1937b040fe21b3e12920e84116cc584fe76a7ef799473540c +size 19547 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 482736f6a3..193049ba0f 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:31b6fd539808d47a692e33948145732b530e465eed7356e3a47bf7da157dcb18 -size 66920 +oid sha256:80436cf3797f7fe641e3312074d29d5385d09e93cf58927702cd5a2d9bda6a40 +size 65249 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 44091f5537..91a9536188 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:ea2bb433c696a382c2006381c987eae227b72a159d03d3e654397b00c909f203 -size 82141 +oid sha256:2928e30fe5c473b93644cced7f8826696b6a8018b10ab96d89152b76c835b006 +size 80674 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 d56d0cb66a..c19f4e5cf7 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:50307dd635bc606def8fe4b80068b7c209e17828718c7a5992127953226cd4e2 -size 71172 +oid sha256:bda29daa05fd2ca70ee96e430e887c02d6a05e62accd298b0206dd1c532a77d3 +size 69548 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 72315f46c2..0b4707fc0f 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:8f0a63b3877b4ce2e7613164e9103a9ba935d64886ccb18646f51e0fd9750885 -size 100976 +oid sha256:991669a0939c78b1d1238cdc5f0197e123e5e21229332c53a7e0d65ff5f11ab7 +size 99624 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 41d2ac5ad9..e8f46dab8a 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:1bfcf8909d3414e517357d82c203c5464071b3f673d7623d4f75b6c18f25e22e -size 65742 +oid sha256:08c51bcd425352f667189a6fdf98067322f90f76d506029f3d8db640c943654f +size 64109 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 ecf72ec264..a987d5874c 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:0d3d27b7a366ac20b287f7587866dba06dc5667ba8cd3b6dc040d8ee7e3adb94 -size 80499 +oid sha256:31b76164389481cbe53ea7caa1b1754202314274a0634b8737ad7023bed62a02 +size 79065 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 17f8b1974c..837304dde2 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:cdf2031afb6d849812555f8b38f6b0f1f65aa959f3cc9e468fdeece65c150858 -size 69980 +oid sha256:d9962689d7ea7eedd28c7dbec66244df0321bcf45238b47fb9beac50ee81b4cd +size 68585 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 780f2f9094..6130f85eed 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:b0034ff36f0d58e30ac75b36f0869dc27f617d9f1dfb1cf00b88c9bf32d23986 -size 98632 +oid sha256:58d5217e29f95074a403572449d54a072d5969852d09c3a1ddacb43949f4a570 +size 97346 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png index 3c218ccbbc..cdb602aae4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b918ce7162d95c873f0029a657ee07fca2e34926e4c3cdb39eaeb10123a08721 -size 25340 +oid sha256:2c2976543550c63540f3e4533685e342ef5775018a37f5e282bf889e42c199e6 +size 24955 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png index e6925f9f5b..148d707197 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d62cf7beeba92ee47193a871becad07f50d27e6f6fa0b85f6c620c3f1b04b6ce -size 24697 +oid sha256:c8eb14a3df5edff4af450b39224e6af6efce20f0734119835e6fbec210491412 +size 24251 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png index 0ee71c18e5..fcf2e1f03a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4decbb84ca2cab1260c41fdeec92fe41671cf09b666657ef6c26469b5b18856a -size 12942 +oid sha256:71381d66fb321f01d6d874c025681489224626ad9700c0f15fe619f73f1a648e +size 12532 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png index 1523f18525..779d297fb6 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dac26ebe7db7c63c3f3c443d80a5eb7c33bc7171872c605d694265113931bfca -size 14305 +oid sha256:cb2ac4d88f51c8c1718a525a5abeccbe2bb19df75a100d504de0cb106ff3117c +size 13857 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png index f2e777c40d..e3e7f9da05 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ce76770f233a9098af885c172147f15acb520c3732cc6ab476e5e0e54fcd0b8 -size 14515 +oid sha256:a3eea2cd53364f9d0c85b803c8faa29f083f9db59123ba6e980a79bff7779402 +size 14113 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png index 03a0d65856..b7daa3bf21 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1290c1fb2023cb95f54b6c8d31b044b52d78d7159bf641080cebc6be77588ce7 -size 12482 +oid sha256:d834acd383dfe8da50426e28e267a1076e5634fc2f96e1e7593cb6a05988600f +size 12074 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png index a9818dae5a..f2a9faa5ab 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bef8e1712b4a6e52e9289e9670dcefa437db08466d74a46c90c5025effa0a83 -size 13867 +oid sha256:a14ccc9724a15a7ae3f477a8cda366be345acf10b7a98ce63c4889bc1708844b +size 13379 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png index 81bb4deb1b..bc3991b8ce 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd9fefe7c4c46dfc419576f04470bd0b36f0ce3fab6d7fbb4d6c676341ba4cb8 -size 14071 +oid sha256:26e4aa8610c1dede0d1b637072e773b5c46fef24bc436b9e7444e26f3ba9814c +size 13689 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png index 6a08be241c..3afa92e837 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:311f06e3b866362380c570d7831b69d72b98de07c3d6c0a1b84abb260362084a -size 197521 +oid sha256:515d5a47d530117ff21cb922d1cb2cb81b22d02c083c51bbff69964f92260bbe +size 197095 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png index 04bf989a7e..d78b959356 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50860f113539dbd61adec9b60ebe0bf43e7c6775c6285caa875987fc0b44e168 -size 198108 +oid sha256:b9f402d6f56e6b9eb8d46add584ef28bf88d4a6c16ae050b7773895faafa8d38 +size 197707 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png index 77e9dad534..6ac98e0150 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:094c1de97c33431390f3ff71d73019d9af9a62ed57ba98c94e53f1f9680851fa -size 210728 +oid sha256:2871bd26b6c21d31ca251761eb5a203468406b4d18c13662fa815c53f015b5e1 +size 210297 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png index c3caba08ff..6f49e5ee89 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a17d0f4bc47305b9c8a3866f70cdddb16fcb5ba10c8007c8739a329025e846a -size 211342 +oid sha256:d9dadffc89e0a66b54a0e6b94006f392569e1907dda2c6d0b6134aad187ff84a +size 210913 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png index 5d8a4a590a..3bcf436edd 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77eda575306e00e2b6fc5b434a3f5c276bf3689266b043fd3b1563784d28e628 -size 125448 +oid sha256:9fe5efbf527ef99de18a44e05edb9654b5bc58c17a2fd2d05f57567fbe27ef2c +size 125021 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png index 5bd06e6364..b76993df30 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc51776a66a9289423196ae9cb39cd57ba718fab2c795631ff1d9e7517cdf763 -size 16580 +oid sha256:7ef585a08fc39f7038e861abe6bbb108c3147eab3445722e5500046deb8afe94 +size 16190 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png index 932ddfb632..9d961aa468 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43ab4873fc5fc812bf18af50cbe620c83b273ee70305b5ea06a7aeabdf8dbc93 -size 137637 +oid sha256:58e3353ce8aecff415158d146347206b1dbf167d15523301847f47c2e81b1c5b +size 137215 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png index 924e24ba8a..32ba291890 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33048dafab286cf0e5c84040c034d93a42d69c234edd53132d918a4a1c135ece -size 137908 +oid sha256:2a6b181d16c5ac2e77d11efa8cfb5a6ce7473deaa502502d97c232311878c46a +size 137497 From a9fe0a356071f3e493805fb8119dc2ff7f6fd96e Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 2 Jun 2026 13:14:03 +0200 Subject: [PATCH 309/471] Fix LiveLocationReceiver interface and update tests --- .../impl/live/service/LiveLocationReceiver.kt | 2 +- .../LiveLocationSharingCoordinatorTest.kt | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt index 90311e7d4a..697a074093 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationReceiver.kt @@ -11,5 +11,5 @@ import io.element.android.features.location.api.Location interface LiveLocationReceiver { suspend fun onLocationUpdate(location: Location) - suspend fun onUnrecoverableError() {} + suspend fun onUnrecoverableError() } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt index f74322b4c5..6cee53a482 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/live/LiveLocationSharingCoordinatorTest.kt @@ -27,7 +27,7 @@ class LiveLocationSharingCoordinatorTest { nowMillis = { 0L }, ) - coordinator.register(A_SESSION_ID, LiveLocationReceiver { }) + coordinator.register(A_SESSION_ID, liveLocationReceiver()) coordinator.unregister(A_SESSION_ID) assertThat(startCount).isEqualTo(1) @@ -43,8 +43,8 @@ class LiveLocationSharingCoordinatorTest { nowMillis = { 4_000L }, ) - coordinator.register(A_SESSION_ID) { error("boom") } - coordinator.register(A_SESSION_ID_2) { location -> delivered += location } + coordinator.register(A_SESSION_ID, liveLocationReceiver { error("boom") }) + coordinator.register(A_SESSION_ID_2, liveLocationReceiver { delivered += it }) coordinator.dispatch(Location(lat = 1.0, lon = 2.0, accuracy = 3f)) assertThat(delivered).containsExactly(Location(lat = 1.0, lon = 2.0, accuracy = 3f)) @@ -60,7 +60,7 @@ class LiveLocationSharingCoordinatorTest { nowMillis = { nowMillis }, ) - coordinator.register(A_SESSION_ID) { location -> delivered += location } + coordinator.register(A_SESSION_ID, liveLocationReceiver { delivered += it }) val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) @@ -79,7 +79,7 @@ class LiveLocationSharingCoordinatorTest { nowMillis = { nowMillis }, ) - coordinator.register(A_SESSION_ID) { location -> delivered += location } + coordinator.register(A_SESSION_ID, liveLocationReceiver { delivered += it }) val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) val secondLocation = Location(lat = 4.0, lon = 5.0, accuracy = 6f) @@ -101,7 +101,7 @@ class LiveLocationSharingCoordinatorTest { nowMillis = { nowMillis }, ) - coordinator.register(A_SESSION_ID) { location -> delivered += location } + coordinator.register(A_SESSION_ID, liveLocationReceiver { delivered += it }) val firstLocation = Location(lat = 1.0, lon = 2.0, accuracy = 3f) val secondLocation = Location(lat = 4.0, lon = 5.0, accuracy = 6f) @@ -113,3 +113,10 @@ class LiveLocationSharingCoordinatorTest { assertThat(delivered).containsExactly(firstLocation, secondLocation).inOrder() } } + +private fun liveLocationReceiver( + onLocation: suspend (Location) -> Unit = {}, +) = object : LiveLocationReceiver { + override suspend fun onLocationUpdate(location: Location) = onLocation(location) + override suspend fun onUnrecoverableError() = Unit +} From be0eb003bf876bd27cca12bd3f6e368ea3370d82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:16:50 +0200 Subject: [PATCH 310/471] Update dependency org.maplibre.gl:android-sdk to v13.2.0 (#6930) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 05530df227..46109a4a01 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -212,7 +212,7 @@ vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil3", 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:13.1.0" +maplibre = "org.maplibre.gl:android-sdk:13.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2" maplibre_compose = "org.maplibre.compose:maplibre-compose:0.13.0" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2" From 1aef3860c277a55de7e2248b83d713902be394ac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jun 2026 15:22:04 +0200 Subject: [PATCH 311/471] [Media bottom sheet] Improve design, and hide Share and Download when rendered from the MediaViewer. Closes #6907 --- .../impl/details/MediaBottomSheetState.kt | 1 + .../MediaBottomSheetStateDetailsProvider.kt | 5 ++ .../impl/details/MediaDetailsBottomSheet.kt | 66 +++++++++---------- .../impl/gallery/MediaGalleryPresenter.kt | 1 + .../impl/viewer/MediaViewerPresenter.kt | 1 + .../impl/viewer/MediaViewerView.kt | 2 + .../details/MediaDetailsBottomSheetTest.kt | 30 +++++++-- .../impl/gallery/MediaGalleryPresenterTest.kt | 2 + .../impl/viewer/MediaViewerViewTest.kt | 22 ------- 9 files changed, 70 insertions(+), 60 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetState.kt index 722126fac6..245e3a14b8 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetState.kt @@ -16,6 +16,7 @@ sealed interface MediaBottomSheetState { data object Hidden : MediaBottomSheetState data class Details( + val fromGallery: Boolean, val eventId: EventId?, val canDelete: Boolean, val mediaInfo: MediaInfo, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt index 99a6925b9d..7540d49b8a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaBottomSheetStateDetailsProvider.kt @@ -29,10 +29,14 @@ open class MediaBottomSheetStateDetailsProvider : PreviewParameterProvider @@ -148,11 +118,41 @@ fun MediaDetailsBottomSheet( onOpenWith(state.eventId) } ) + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.VisibilityOn())), + headlineContent = { Text(stringResource(CommonStrings.action_view_in_timeline)) }, + onClick = { + onViewInTimeline(state.eventId) + } + ) + if (state.fromGallery) { + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ShareAndroid())), + headlineContent = { Text(stringResource(CommonStrings.action_share)) }, + onClick = { + onShare(state.eventId) + } + ) + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Download())), + headlineContent = { Text(stringResource(CommonStrings.action_download)) }, + onClick = { + onDownload(state.eventId) + } + ) + } + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Forward())), + headlineContent = { Text(stringResource(CommonStrings.action_forward)) }, + onClick = { + onForward(state.eventId) + } + ) if (state.canDelete) { HorizontalDivider() ListItem( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Delete())), - headlineContent = { Text(stringResource(CommonStrings.action_delete)) }, + headlineContent = { Text(stringResource(CommonStrings.action_delete_file)) }, style = ListItemStyle.Destructive, onClick = { onDelete(state.eventId) @@ -216,16 +216,14 @@ private fun SenderRow( } @Composable -private fun ColumnScope.Title() { +private fun Title() { Text( modifier = Modifier - .align(Alignment.CenterHorizontally) .padding(top = 16.dp, bottom = 8.dp, start = 16.dp, end = 16.dp) .semantics { heading() }, text = stringResource(R.string.screen_media_details_title), - textAlign = TextAlign.Center, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index 2c2fb31020..25e27330b4 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -129,6 +129,7 @@ class MediaGalleryPresenter( } is MediaGalleryEvent.OpenInfo -> coroutineScope.launch { mediaBottomSheetState = MediaBottomSheetState.Details( + fromGallery = true, eventId = event.mediaItem.eventId(), canDelete = when (event.mediaItem.mediaInfo().senderId) { null -> false diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index 296f20ef4c..eec99468f7 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -145,6 +145,7 @@ class MediaViewerPresenter( } is MediaViewerEvent.OpenInfo -> coroutineScope.launch { mediaBottomSheetState = MediaBottomSheetState.Details( + fromGallery = false, eventId = event.data.eventId, canDelete = when (event.data.mediaInfo.senderId) { null -> false diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 3a7c006593..09206a318d 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -277,6 +277,7 @@ fun MediaViewerView( state.eventSink(MediaViewerEvent.ViewInTimeline(it)) }, onShare = { + // Note: share action is not rendered when the bottom sheet is opened from the media viewer (currentData as? MediaViewerPageData.MediaViewerData)?.let { state.eventSink(MediaViewerEvent.Share(currentData)) } @@ -285,6 +286,7 @@ fun MediaViewerView( state.eventSink(MediaViewerEvent.Forward(it)) }, onDownload = { + // Note: download action is not rendered when the bottom sheet is opened from the media viewer (currentData as? MediaViewerPageData.MediaViewerData)?.let { state.eventSink(MediaViewerEvent.SaveOnDisk(currentData)) } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt index 5b0f105aea..c6e317a853 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheetTest.kt @@ -45,7 +45,9 @@ class MediaDetailsBottomSheetTest { @Test @Config(qualifiers = "h1024dp") fun `clicking on Share invokes expected callback`() = runAndroidComposeUiTest { - val state = aMediaBottomSheetStateDetails() + val state = aMediaBottomSheetStateDetails( + fromGallery = true, + ) ensureCalledOnceWithParam(state.eventId) { callback -> setMediaDetailsBottomSheet( state = state, @@ -55,6 +57,15 @@ class MediaDetailsBottomSheetTest { } } + @Test + @Config(qualifiers = "h1024dp") + fun `item Share is not displayed when opened from the media viewer`() = runAndroidComposeUiTest { + setMediaDetailsBottomSheet( + state = aMediaBottomSheetStateDetails(), + ) + onNodeWithText(activity!!.getString(CommonStrings.action_share)).assertDoesNotExist() + } + @Test @Config(qualifiers = "h1024dp") fun `clicking on Forward invokes expected callback`() = runAndroidComposeUiTest { @@ -71,7 +82,9 @@ class MediaDetailsBottomSheetTest { @Test @Config(qualifiers = "h1024dp") fun `clicking on Download invokes expected callback`() = runAndroidComposeUiTest { - val state = aMediaBottomSheetStateDetails() + val state = aMediaBottomSheetStateDetails( + fromGallery = true, + ) ensureCalledOnceWithParam(state.eventId) { callback -> setMediaDetailsBottomSheet( state = state, @@ -81,6 +94,15 @@ class MediaDetailsBottomSheetTest { } } + @Test + @Config(qualifiers = "h1024dp") + fun `item Download is not displayed when opened from the media viewer`() = runAndroidComposeUiTest { + setMediaDetailsBottomSheet( + state = aMediaBottomSheetStateDetails(), + ) + onNodeWithText(activity!!.getString(CommonStrings.action_download)).assertDoesNotExist() + } + @Config(qualifiers = "h1024dp") @Test fun `clicking on Delete invokes expected callback`() = runAndroidComposeUiTest { @@ -90,8 +112,8 @@ class MediaDetailsBottomSheetTest { state = state, onDelete = callback, ) - onNodeWithText(activity!!.getString(CommonStrings.action_delete)).assertExists() - clickOn(CommonStrings.action_delete) + onNodeWithText(activity!!.getString(CommonStrings.action_delete_file)).assertExists() + clickOn(CommonStrings.action_delete_file) } } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt index 929f2a970e..3ab95f2703 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt @@ -137,6 +137,7 @@ class MediaGalleryPresenterTest { val state = awaitItem() assertThat(state.mediaBottomSheetState).isEqualTo( MediaBottomSheetState.Details( + fromGallery = true, eventId = AN_EVENT_ID, canDelete = canDeleteOwn, mediaInfo = item.mediaInfo, @@ -184,6 +185,7 @@ class MediaGalleryPresenterTest { val state = awaitItem() assertThat(state.mediaBottomSheetState).isEqualTo( MediaBottomSheetState.Details( + fromGallery = true, eventId = AN_EVENT_ID, canDelete = canDeleteOther, mediaInfo = item.mediaInfo, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index 6521f450f5..092ad65c41 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -135,28 +135,6 @@ class MediaViewerViewTest { ) } - @Test - @Config(qualifiers = "h1024dp") - fun `clicking on download emits expected Event`() { - val data = aMediaViewerPageData() - testBottomSheetAction( - data, - CommonStrings.action_download, - MediaViewerEvent.SaveOnDisk(data), - ) - } - - @Test - @Config(qualifiers = "h1024dp") - fun `clicking on share emits expected Event`() { - val data = aMediaViewerPageData() - testBottomSheetAction( - data, - CommonStrings.action_share, - MediaViewerEvent.Share(data), - ) - } - @Config(qualifiers = "h1024dp") @Test fun `clicking on open in emits expected Event`() { From e36115e28a07fb5e0a73e307825bbccebd66a000 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 2 Jun 2026 13:37:23 +0000 Subject: [PATCH 312/471] Update screenshots --- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en.png | 4 ++-- ...iaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en.png | 3 +++ ...viewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_3_en.png | 4 ++-- ...viewer.impl.details_MediaDetailsBottomSheet_Night_4_en.png | 3 +++ ...ies.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png | 4 ++-- ...s.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png | 4 ++-- ...mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en.png | 4 ++-- ...ibraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png | 4 ++-- 14 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en.png create mode 100644 tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png index c187b29009..12e89a638f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be6223e16a2b9936164ec4ba659129ceb80ed5746e3376f32559b4942b271502 -size 40656 +oid sha256:3239eba4debc9249ecd75a11f9f96aa543ea0bab3f527a654a5ea58e07b942fb +size 40364 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png index c187b29009..28088c2bcd 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be6223e16a2b9936164ec4ba659129ceb80ed5746e3376f32559b4942b271502 -size 40656 +oid sha256:e2c67d25e8e74b9ab7ebcff3d167bc72de2bc2ae7ef827c7785c64b551426eb7 +size 38652 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png index b95ce472ff..8857d18f8d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b57d412a5815756b453d907543bc74b4a154814633d8664a3dcffaa0057c7279 -size 44962 +oid sha256:7c7aebf16d23748e4a0a3a035c6ff1d67277d11fac223e2fb2bf66fdbc13ac99 +size 44576 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en.png index 69da701004..ca2d61a8ee 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6144f221b9d11c70d15b54321bfbff3d1de1454e6c73be34d7b2e82bd1625a94 -size 30701 +oid sha256:a335f858bbca40a0bdb3bd239fdc3625975d44ddc45bb653815f20acde9ffd0f +size 30710 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en.png new file mode 100644 index 0000000000..493603dc42 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90ed265904cdf988d9b2ef6b5695d5793c48fa9d33d4db8c6f4bf94c3055844d +size 40720 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png index a86d65d8f8..6c803533e5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e3e43f4dbd1bf0e271de57c962b93100720439d608a7d552669ede42141dbc1 -size 39705 +oid sha256:73ee558ceb73554e8ab72797797a3e4fb4ae60bc805d9603cc23a5ec3325ae71 +size 39361 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png index a86d65d8f8..d7166ccf37 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e3e43f4dbd1bf0e271de57c962b93100720439d608a7d552669ede42141dbc1 -size 39705 +oid sha256:16b6c38de0dcb40f5141f06a8957c6a64c406ec45765a38a5a371f873cca676f +size 37330 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png index 782df4dbaa..7f9ac4954f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:086c64f4094cb2e0fda8e415a0d8d89b7dafe67f4d42088eded7abbf45b8de93 -size 44088 +oid sha256:4e37123303553d032b579ce0cdace8c6f2aa6c32b3ccce8ef04abac4c84e16b7 +size 43659 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en.png index 2534bd5d0c..c4af30aa2a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2898f00a828a69ce9cdd56994d7878f6aae8c5b2ea5d1150df57aa2ebd7e537b -size 29233 +oid sha256:423e92ee408ca6c3ab7c1071245810fcbc2d25a7ce62b3068f1616c933ad008c +size 29216 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en.png new file mode 100644 index 0000000000..e79c1a45fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5fd5c4d8d9d2ca9709a2b03d423899ef99738b4ba2e081d9456168605e65e07 +size 39788 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png index 6c70063773..8c9923d1d1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad4bf14c2f6d2d36c978d90c7641fc8b3a9c71062d6b23362eea12c0b7ff24e4 -size 40572 +oid sha256:0c8c1dd110d367173c8a918ff9f94aa4e67e9bcba6d417e8ddd0b6660f6ecf20 +size 40280 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png index 40d55d29a1..5c5cbf2dc5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b44ce089c932019d58e7f1c993f80871352878adf0f5a133a72edce18f5bcb9 -size 39496 +oid sha256:00c97219fdc6377f9f965635b3ed696f3595f6de413528a96ea05d2c4b385c9a +size 39155 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en.png index 41e45ad002..c45563c143 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e641d1d6604b6c5f489ab38438d9f3b8dcea9802113011200e9ea589c4e2dbee -size 25310 +oid sha256:5a9b7b0c427f40aa8f0a61025fdcfb8093c611e15ef10f22576545a64c6ec35c +size 25286 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png index 40d55d29a1..5c5cbf2dc5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b44ce089c932019d58e7f1c993f80871352878adf0f5a133a72edce18f5bcb9 -size 39496 +oid sha256:00c97219fdc6377f9f965635b3ed696f3595f6de413528a96ea05d2c4b385c9a +size 39155 From 13f9ecca1854a6ac79f26392cb434f03f985f3f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 13:57:16 +0000 Subject: [PATCH 313/471] Update dependency org.unifiedpush.android:connector to v3.3.3 (#6937) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 46109a4a01..2ed618ee0e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -207,7 +207,7 @@ sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version. sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" } sqlcipher = "net.zetetic:sqlcipher-android:4.16.0" sqlite = "androidx.sqlite:sqlite-ktx:2.6.2" -unifiedpush = "org.unifiedpush.android:connector:3.3.2" +unifiedpush = "org.unifiedpush.android:connector:3.3.3" vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil3", version.ref = "telephoto" } telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" } From 748454f34da44414e2215a2be5570d542659d42d Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 13:33:04 -0700 Subject: [PATCH 314/471] Add shared PasswordVisibilityToggle design-system component Extract the show/hide password trailing-icon into a reusable PasswordVisibilityToggle so every password field reveals plaintext the same way and announces the same a11y labels. Reuse it from the login and reset-identity password fields. Pure refactor, no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../loginpassword/LoginPasswordView.kt | 15 +++----- .../password/ResetIdentityPasswordView.kt | 16 +++----- .../components/PasswordVisibilityToggle.kt | 37 +++++++++++++++++++ 3 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/PasswordVisibilityToggle.kt diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index d3641ea749..feb1d3d53a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -60,6 +60,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.PasswordVisibilityToggle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -249,16 +250,10 @@ private fun LoginForm( placeholder = stringResource(CommonStrings.common_password), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), trailingIcon = { - val image = - if (passwordVisible) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff() - val description = - if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) - Box(Modifier.clickable { passwordVisible = !passwordVisible }) { - Icon( - imageVector = image, - contentDescription = description, - ) - } + PasswordVisibilityToggle( + visible = passwordVisible, + onToggle = { passwordVisible = !passwordVisible }, + ) }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt index 8d32433ac5..3bb77ac206 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt @@ -8,8 +8,6 @@ package io.element.android.features.securebackup.impl.reset.password -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -32,7 +30,7 @@ import io.element.android.libraries.designsystem.modifiers.onTabOrEnterKeyFocusN 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.Icon +import io.element.android.libraries.designsystem.theme.components.PasswordVisibilityToggle import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TextFieldValidity import io.element.android.libraries.ui.strings.CommonStrings @@ -92,14 +90,10 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool singleLine = true, visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(), trailingIcon = { - val image = - if (showPassword) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff() - val description = - if (showPassword) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) - - Box(Modifier.clickable { showPassword = !showPassword }) { - Icon(imageVector = image, description) - } + PasswordVisibilityToggle( + visible = showPassword, + onToggle = { showPassword = !showPassword }, + ) }, validity = if (hasError) TextFieldValidity.Invalid else TextFieldValidity.None, supportingText = if (hasError) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/PasswordVisibilityToggle.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/PasswordVisibilityToggle.kt new file mode 100644 index 0000000000..9154ec6292 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/PasswordVisibilityToggle.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.theme.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.ui.strings.CommonStrings + +/** + * Show/hide toggle for a password [TextField], intended for its `trailingIcon` slot. + * Shared so every password field reveals plaintext the same way and announces the + * same accessibility labels. + */ +@Composable +fun PasswordVisibilityToggle( + visible: Boolean, + onToggle: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier.clickable(onClick = onToggle)) { + Icon( + imageVector = if (visible) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff(), + contentDescription = stringResource( + if (visible) CommonStrings.a11y_hide_password else CommonStrings.a11y_show_password + ), + ) + } +} From 2931418018d1f6b15cf542361f412ae2e2d74cf8 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 13:33:18 -0700 Subject: [PATCH 315/471] Parse custom_recovery_passphrase_settings from Element .well-known Add CustomRecoveryPassphraseRequirements to ElementWellKnown and decode the custom_recovery_passphrase_settings block (currently just min_character_count) from the homeserver's element.json. Invalid specs (missing or non-positive min_character_count) are logged and mapped to null so the setup flow falls back to the generated-key path. Field names match the ESS well-known schema (element-enterprise#261). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../CustomRecoveryPassphraseRequirements.kt | 23 ++++++ .../wellknown/api/ElementWellKnown.kt | 1 + .../impl/InternalElementWellKnown.kt | 18 ++++- .../libraries/wellknown/impl/Mapper.kt | 18 +++++ ...ustomRecoveryPassphraseRequirementsTest.kt | 33 ++++++++ .../DefaultSessionWellknownRetrieverTest.kt | 80 +++++++++++++++++++ .../features/wellknown/test/Fixtures.kt | 9 +++ 7 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirements.kt create mode 100644 libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirementsTest.kt diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirements.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirements.kt new file mode 100644 index 0000000000..dc6241b0a9 --- /dev/null +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirements.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.wellknown.api + +/** + * Server-driven requirements for a user-chosen recovery passphrase. Today the only rule + * is a minimum character count; additional rules can be added here as the well-known + * schema (`custom_recovery_passphrase_settings`) grows. + */ +data class CustomRecoveryPassphraseRequirements( + val minCharacterCount: Int, +) { + /** True when [input] meets every active rule. */ + fun isSatisfiedBy(input: String): Boolean = isSatisfiedBy(input.length) + + /** True when an input of [length] characters meets every active rule. */ + fun isSatisfiedBy(length: Int): Boolean = length >= minCharacterCount +} diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt index 4c1c476c7a..ac050ba94a 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/ElementWellKnown.kt @@ -15,4 +15,5 @@ data class ElementWellKnown( val brandColor: String?, val notificationSound: String?, val identityProviderAppScheme: String?, + val customRecoveryPassphraseRequirements: CustomRecoveryPassphraseRequirements?, ) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt index 2e2b5de16f..803e498eda 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/InternalElementWellKnown.kt @@ -15,7 +15,15 @@ import kotlinx.serialization.Serializable * Example: *
  * {
- *     "registration_helper_url": "https://element.io"
+ *     "registration_helper_url": "https://element.io",
+ *     "enforce_element_pro": true,
+ *     "rageshake_url": "https://example.org/rageshake",
+ *     "brand_color": "#FF0000",
+ *     "notification_sound": "ring.flac",
+ *     "idp_app_scheme": "io.element.app",
+ *     "custom_recovery_passphrase_settings": {
+ *         "min_character_count": 8
+ *     }
  * }
  * 
* . @@ -34,4 +42,12 @@ data class InternalElementWellKnown( val notificationSound: String? = null, @SerialName("idp_app_scheme") val identityProviderAppScheme: String? = null, + @SerialName("custom_recovery_passphrase_settings") + val customRecoveryPassphraseRequirements: InternalCustomRecoveryPassphraseRequirements? = null, +) + +@Serializable +data class InternalCustomRecoveryPassphraseRequirements( + @SerialName("min_character_count") + val minCharacterCount: Int? = null, ) diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt index 41ed54d7db..61899578d7 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt @@ -8,7 +8,12 @@ package io.element.android.libraries.wellknown.impl +import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements import io.element.android.libraries.wellknown.api.ElementWellKnown +import timber.log.Timber + +private val loggerTag = LoggerTag("Wellknown") internal fun InternalElementWellKnown.map() = ElementWellKnown( registrationHelperUrl = registrationHelperUrl, @@ -17,4 +22,17 @@ internal fun InternalElementWellKnown.map() = ElementWellKnown( brandColor = brandColor, notificationSound = notificationSound, identityProviderAppScheme = identityProviderAppScheme, + customRecoveryPassphraseRequirements = customRecoveryPassphraseRequirements?.toPublic(), ) + +private fun InternalCustomRecoveryPassphraseRequirements.toPublic(): CustomRecoveryPassphraseRequirements? { + val min = minCharacterCount ?: run { + Timber.tag(loggerTag.value).w("custom_recovery_passphrase_settings missing min_character_count; ignoring spec") + return null + } + if (min <= 0) { + Timber.tag(loggerTag.value).w("custom_recovery_passphrase_settings.min_character_count must be > 0; ignoring spec") + return null + } + return CustomRecoveryPassphraseRequirements(minCharacterCount = min) +} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirementsTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirementsTest.kt new file mode 100644 index 0000000000..c8f10fe2ca --- /dev/null +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/api/CustomRecoveryPassphraseRequirementsTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.wellknown.api + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class CustomRecoveryPassphraseRequirementsTest { + @Test + fun `empty input never satisfies a positive minimum`() { + assertThat(CustomRecoveryPassphraseRequirements(minCharacterCount = 1).isSatisfiedBy("")).isFalse() + } + + @Test + fun `input shorter than minimum is not satisfied`() { + assertThat(CustomRecoveryPassphraseRequirements(minCharacterCount = 8).isSatisfiedBy("abc")).isFalse() + } + + @Test + fun `input exactly at minimum is satisfied`() { + assertThat(CustomRecoveryPassphraseRequirements(minCharacterCount = 8).isSatisfiedBy("abcdefgh")).isTrue() + } + + @Test + fun `input longer than minimum is satisfied`() { + assertThat(CustomRecoveryPassphraseRequirements(minCharacterCount = 4).isSatisfiedBy("abcdefgh")).isTrue() + } +} diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index cca40e283f..68d4d818c2 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.services.toolbox.api.systemclock.SystemClock @@ -51,6 +52,7 @@ class DefaultSessionWellknownRetrieverTest { brandColor = null, notificationSound = null, identityProviderAppScheme = null, + customRecoveryPassphraseRequirements = null, ) ) ) @@ -76,6 +78,7 @@ class DefaultSessionWellknownRetrieverTest { brandColor = "#FF0000", notificationSound = "a_notification_sound.flac", identityProviderAppScheme = "an_app_scheme", + customRecoveryPassphraseRequirements = null, ) ) ) @@ -105,11 +108,86 @@ class DefaultSessionWellknownRetrieverTest { brandColor = null, notificationSound = null, identityProviderAppScheme = null, + customRecoveryPassphraseRequirements = null, ) ) ) } + @Test + fun `get element wellknown with custom recovery passphrase requirements`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success( + """{ + "custom_recovery_passphrase_settings": { + "min_character_count": 8 + } + }""".trimIndent().toByteArray() + ) + }, + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success( + anElementWellKnown( + customRecoveryPassphraseRequirements = CustomRecoveryPassphraseRequirements(minCharacterCount = 8) + ) + ) + ) + } + + @Test + fun `get element wellknown with custom recovery passphrase requirements missing min character count maps to null`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success( + """{ + "custom_recovery_passphrase_settings": {} + }""".trimIndent().toByteArray() + ) + }, + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success(anElementWellKnown()) + ) + } + + @Test + fun `get element wellknown with non-positive min character count maps to null`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success( + """{ + "custom_recovery_passphrase_settings": { + "min_character_count": 0 + } + }""".trimIndent().toByteArray() + ) + }, + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success(anElementWellKnown()) + ) + } + + @Test + fun `get element wellknown with negative min character count maps to null`() = runTest { + val sut = createDefaultSessionWellknownRetriever( + getUrlLambda = { + Result.success( + """{ + "custom_recovery_passphrase_settings": { + "min_character_count": -5 + } + }""".trimIndent().toByteArray() + ) + }, + ) + assertThat(sut.getElementWellKnown()).isEqualTo( + WellknownRetrieverResult.Success(anElementWellKnown()) + ) + } + @Test fun `get element wellknown json error`() = runTest { val sut = createDefaultSessionWellknownRetriever( @@ -157,6 +235,7 @@ class DefaultSessionWellknownRetrieverTest { brandColor = "#FF0000", notificationSound = "a_notification_sound.flac", identityProviderAppScheme = "an_app_scheme", + customRecoveryPassphraseRequirements = null, ) ) ) @@ -210,6 +289,7 @@ class DefaultSessionWellknownRetrieverTest { brandColor = "#FF0000", notificationSound = "a_notification_sound.flac", identityProviderAppScheme = "an_app_scheme", + customRecoveryPassphraseRequirements = null, ) ) ) diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt index ae7d1c629c..1ed69207ba 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/Fixtures.kt @@ -8,6 +8,7 @@ package io.element.android.features.wellknown.test +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements import io.element.android.libraries.wellknown.api.ElementWellKnown fun anElementWellKnown( @@ -17,6 +18,7 @@ fun anElementWellKnown( brandColor: String? = null, notificationSound: String? = null, identityProviderAppScheme: String? = null, + customRecoveryPassphraseRequirements: CustomRecoveryPassphraseRequirements? = null, ) = ElementWellKnown( registrationHelperUrl = registrationHelperUrl, enforceElementPro = enforceElementPro, @@ -24,4 +26,11 @@ fun anElementWellKnown( brandColor = brandColor, notificationSound = notificationSound, identityProviderAppScheme = identityProviderAppScheme, + customRecoveryPassphraseRequirements = customRecoveryPassphraseRequirements, +) + +fun aCustomRecoveryPassphraseRequirements( + minCharacterCount: Int = 8, +) = CustomRecoveryPassphraseRequirements( + minCharacterCount = minCharacterCount, ) From 62ac6a3d2f6f6a2fa6c9201fdf54f96386acdc2f Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 13:51:59 -0700 Subject: [PATCH 316/471] Add enterprise gate for custom recovery passphrase Add isCustomRecoveryPassphraseEnabled() and estimateCustomRecoveryPassphraseStrength() to EnterpriseService, plus the CustomRecoveryPassphraseStrength result types. FOSS builds return false/null so the feature stays gated off; the enterprise implementation (estimator) lands separately in the enterprise repo. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../api/CustomRecoveryPassphraseStrength.kt | 35 +++++++++++++++++++ .../enterprise/api/EnterpriseService.kt | 16 +++++++++ .../impl/DefaultEnterpriseService.kt | 5 +++ .../enterprise/test/FakeEnterpriseService.kt | 8 +++++ 4 files changed, 64 insertions(+) create mode 100644 features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/CustomRecoveryPassphraseStrength.kt diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/CustomRecoveryPassphraseStrength.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/CustomRecoveryPassphraseStrength.kt new file mode 100644 index 0000000000..ea8b02d93f --- /dev/null +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/CustomRecoveryPassphraseStrength.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.enterprise.api + +/** + * Strength tier surfaced to the user under a custom recovery passphrase field. + * The eight tiers mirror iOS `PasswordStrengthThreshold` 1:1 so both clients label a given + * passphrase identically; each maps to its own label and time-to-crack description in the UI. + */ +enum class CustomRecoveryPassphraseStrength { + Garbage, + Weak, + Moderate, + Okay, + Strong, + VeryStrong, + UltraStrong, + Mega, +} + +/** + * Result of estimating a custom recovery passphrase's strength. The estimation algorithm itself + * is an enterprise capability ([EnterpriseService.estimateCustomRecoveryPassphraseStrength]); FOSS + * builds never produce one. + */ +data class CustomRecoveryPassphraseStrengthResult( + val strength: CustomRecoveryPassphraseStrength, + /** Fill amount for the progress bar. Always in `0f..1f`. */ + val score: Float, +) diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 92d8b9b646..ce56a5dc80 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -41,6 +41,22 @@ interface EnterpriseService { */ fun getNoisyNotificationChannelId(sessionId: SessionId): String? + /** + * Whether the user is allowed to set a custom recovery passphrase. + * Enterprise builds gate this in addition to the homeserver's .well-known `customRecoveryPassphraseRequirements`; + * when false the setup flow falls back to the auto-generated key path even if the homeserver + * advertises a spec. + */ + fun isCustomRecoveryPassphraseEnabled(): Boolean + + /** + * Estimate the strength of a user-typed custom recovery [passphrase]. + * The estimation algorithm is an enterprise capability: FOSS builds return null, so callers + * should treat null as "no indicator to show". Callers are responsible for not calling this with + * an empty passphrase if they want the indicator hidden while the field is empty. + */ + fun estimateCustomRecoveryPassphraseStrength(passphrase: String): CustomRecoveryPassphraseStrengthResult? + companion object { const val ANY_ACCOUNT_PROVIDER = "*" } diff --git a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 6e3ed5d3cc..caa2eb291f 100644 --- a/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl-foss/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -13,6 +13,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl +import io.element.android.features.enterprise.api.CustomRecoveryPassphraseStrengthResult import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.flow.Flow @@ -45,4 +46,8 @@ class DefaultEnterpriseService : EnterpriseService { } override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null + + override fun isCustomRecoveryPassphraseEnabled(): Boolean = false + + override fun estimateCustomRecoveryPassphraseStrength(passphrase: String): CustomRecoveryPassphraseStrengthResult? = null } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index 805c75be6a..fb474a96e5 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -11,6 +11,7 @@ package io.element.android.features.enterprise.test import androidx.compose.ui.graphics.Color import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.features.enterprise.api.BugReportUrl +import io.element.android.features.enterprise.api.CustomRecoveryPassphraseStrengthResult import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.tests.testutils.lambda.lambdaError @@ -31,6 +32,8 @@ class FakeEnterpriseService( private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() }, + private val isCustomRecoveryPassphraseEnabledResult: Boolean = false, + private val estimateCustomRecoveryPassphraseStrengthResult: (String) -> CustomRecoveryPassphraseStrengthResult? = { null }, ) : EnterpriseService { private val brandColorState = MutableStateFlow(initialBrandColor) private val semanticColorsState = MutableStateFlow(initialSemanticColors) @@ -79,4 +82,9 @@ class FakeEnterpriseService( override fun getNoisyNotificationChannelId(sessionId: SessionId): String? { return getNoisyNotificationChannelIdResult(sessionId) } + + override fun isCustomRecoveryPassphraseEnabled(): Boolean = isCustomRecoveryPassphraseEnabledResult + + override fun estimateCustomRecoveryPassphraseStrength(passphrase: String): CustomRecoveryPassphraseStrengthResult? = + estimateCustomRecoveryPassphraseStrengthResult(passphrase) } From 0cb9d2911de86ed5407c3c4965055a91bd197656 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 13:52:08 -0700 Subject: [PATCH 317/471] Allow enableRecovery to derive the key from a passphrase enableRecovery now takes an optional passphrase and returns the recovery key as the suspend result (like resetRecoveryKey), avoiding the flow/return-value race. For the passphrase path the SDK-derived base58 key is kept out of the session-scoped progress flow so it is never surfaced to the user; the caller receives it as the return value and is responsible for scrubbing it. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../api/encryption/EncryptionService.kt | 10 ++++++++-- .../impl/encryption/RustEncryptionService.kt | 19 ++++++++++++++----- .../test/encryption/FakeEncryptionService.kt | 9 +++++---- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index 333cfb709b..f82cdc1969 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -24,9 +24,15 @@ interface EncryptionService { suspend fun enableBackups(): Result /** - * Enable recovery. Observe enableProgressStateFlow to get progress and recovery key. + * Enable recovery and return the SDK-generated recovery key on success. + * Observe [enableRecoveryProgressStateFlow] for in-progress UI updates. + * + * @param waitForBackupsToUpload when true, suspends until existing room keys finish uploading. + * @param passphrase optional user-supplied passphrase. When set, the SDK derives the + * secret-storage key from it instead of a random base58 key; the passphrase can later be + * passed to [recover], and the returned base58 key should not be surfaced to the user. */ - suspend fun enableRecovery(waitForBackupsToUpload: Boolean): Result + suspend fun enableRecovery(waitForBackupsToUpload: Boolean, passphrase: String? = null): Result /** * Change the recovery and return the new recovery key. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index f67b7cb7e1..74f235f9f8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -131,19 +131,28 @@ class RustEncryptionService( override suspend fun enableRecovery( waitForBackupsToUpload: Boolean, - ): Result = withContext(dispatchers.io) { + passphrase: String?, + ): Result = withContext(dispatchers.io) { runCatchingExceptions { - service.enableRecovery( + // The key arrives as the suspend return value (like resetRecoveryKey), avoiding a + // flow/return-value race; the listener only feeds sub-progress. + enableRecoveryProgressStateFlow.value = EnableRecoveryProgress.Starting + val key = service.enableRecovery( waitForBackupsToUpload = waitForBackupsToUpload, progressListener = object : EnableRecoveryProgressListener { override fun onUpdate(status: RustEnableRecoveryProgress) { enableRecoveryProgressStateFlow.value = enableRecoveryProgressMapper.map(status) } }, - passphrase = null, + passphrase = passphrase, ) - // enableRecovery returns the encryption key, but we read it from the state flow - .let { } + // Pin Done explicitly so observers get a coherent terminal value. For the passphrase + // path the user never sees the SDK base58 key, so keep it out of this session-scoped + // (in-memory) flow entirely; the caller still receives it as the return value and is + // responsible for scrubbing it. The auto-generated path must retain the key here since + // that is how it is surfaced to the user. + enableRecoveryProgressStateFlow.value = EnableRecoveryProgress.Done(if (passphrase != null) "" else key) + key }.mapFailure { it.mapRecoveryException() } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index 04e3779298..e090319821 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -28,7 +28,8 @@ class FakeEncryptionService( private val pinUserIdentityResult: (UserId) -> Result = { lambdaError() }, private val withdrawVerificationResult: (UserId) -> Result = { lambdaError() }, private val getUserIdentityResult: (UserId) -> Result = { lambdaError() }, - private val enableRecoveryLambda: (Boolean) -> Result = { lambdaError() }, + private val enableRecoveryLambda: (Boolean, String?) -> Result = { _, _ -> lambdaError() }, + private val resetRecoveryKeyLambda: () -> Result = { Result.success(FAKE_RECOVERY_KEY) }, ) : EncryptionService { private var disableRecoveryFailure: Exception? = null override val backupStateStateFlow: MutableStateFlow = MutableStateFlow(BackupState.UNKNOWN) @@ -90,11 +91,11 @@ class FakeEncryptionService( } override suspend fun resetRecoveryKey(): Result = simulateLongTask { - return Result.success(FAKE_RECOVERY_KEY) + return resetRecoveryKeyLambda() } - override suspend fun enableRecovery(waitForBackupsToUpload: Boolean): Result = simulateLongTask { - return enableRecoveryLambda(waitForBackupsToUpload) + override suspend fun enableRecovery(waitForBackupsToUpload: Boolean, passphrase: String?): Result = simulateLongTask { + return enableRecoveryLambda(waitForBackupsToUpload, passphrase) } fun givenWaitForBackupUploadSteadyStateFlow(flow: Flow) { From db6f4414df4de42c91415f8b91c8493637648a92 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 13:52:21 -0700 Subject: [PATCH 318/471] Add custom recovery passphrase setup flow When the homeserver advertises custom_recovery_passphrase_settings (and the enterprise gate is enabled), the secure-backup setup lets the user enter and confirm their own recovery key with a live strength indicator, instead of receiving a generated one. The SDK derives the 4S key from the passphrase and the base58 key is scrubbed everywhere so it is never shown. Falls back to the generated-key flow when no spec is present or the well-known fetch fails. Snapshot PNGs are intentionally left out; the core team regenerates them after the PR is opened. Co-Authored-By: Claude Opus 4.8 (1M context) --- features/securebackup/impl/build.gradle.kts | 3 + .../impl/setup/CustomPassphraseDerivations.kt | 51 ++ .../impl/setup/SecureBackupSetupEvents.kt | 18 + .../impl/setup/SecureBackupSetupPresenter.kt | 206 ++++- .../impl/setup/SecureBackupSetupState.kt | 24 + .../setup/SecureBackupSetupStateMachine.kt | 6 + .../setup/SecureBackupSetupStateProvider.kt | 140 +++- .../impl/setup/SecureBackupSetupView.kt | 378 ++++++++- .../impl/src/main/res/values/temporary.xml | 37 + .../setup/SecureBackupSetupPresenterTest.kt | 758 +++++++++++++++++- .../android/libraries/testtags/TestTags.kt | 2 + 11 files changed, 1552 insertions(+), 71 deletions(-) create mode 100644 features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/CustomPassphraseDerivations.kt create mode 100644 features/securebackup/impl/src/main/res/values/temporary.xml diff --git a/features/securebackup/impl/build.gradle.kts b/features/securebackup/impl/build.gradle.kts index 54d87ef22e..d7dafc3e65 100644 --- a/features/securebackup/impl/build.gradle.kts +++ b/features/securebackup/impl/build.gradle.kts @@ -38,9 +38,12 @@ dependencies { implementation(projects.libraries.oauth.api) implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) + implementation(projects.libraries.wellknown.api) api(libs.statemachine) api(projects.features.securebackup.api) testCommonDependencies(libs, true) + testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.wellknown.test) } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/CustomPassphraseDerivations.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/CustomPassphraseDerivations.kt new file mode 100644 index 0000000000..7bf6e5805c --- /dev/null +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/CustomPassphraseDerivations.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.securebackup.impl.setup + +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements + +/** + * Validation flags computed once and shared by the presenter and the preview provider. + * + * Note: passphrase strength is intentionally NOT derived here. The estimation algorithm is an + * enterprise capability ([io.element.android.features.enterprise.api.EnterpriseService.estimateCustomRecoveryPassphraseStrength]); + * the presenter computes it via the injected service and the state provider supplies samples directly. + */ +internal data class CustomPassphraseDerivations( + val meetsMinLength: Boolean, + val mismatch: Boolean, + val canContinueFromEntry: Boolean, + val canSubmitCustomPassphrase: Boolean, +) + +internal fun deriveCustomPassphraseState( + requirements: CustomRecoveryPassphraseRequirements?, + passphrase: String, + confirm: String, + step: CustomEntryStep, + setupState: SetupState, +): CustomPassphraseDerivations { + val meetsMinLength = requirements?.isSatisfiedBy(passphrase) ?: true + val mismatch = confirm.isNotEmpty() && passphrase != confirm + val canContinueFromEntry = requirements != null && + passphrase.isNotEmpty() && + meetsMinLength && + setupState is SetupState.Init + val canSubmit = requirements != null && + passphrase.isNotEmpty() && + passphrase == confirm && + meetsMinLength && + step == CustomEntryStep.Confirm && + setupState is SetupState.Init + return CustomPassphraseDerivations( + meetsMinLength = meetsMinLength, + mismatch = mismatch, + canContinueFromEntry = canContinueFromEntry, + canSubmitCustomPassphrase = canSubmit, + ) +} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt index f61e65ba61..faf3d618fe 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvents.kt @@ -13,4 +13,22 @@ sealed interface SecureBackupSetupEvents { data object RecoveryKeyHasBeenSaved : SecureBackupSetupEvents data object Done : SecureBackupSetupEvents data object DismissDialog : SecureBackupSetupEvents + + /** Update the user-typed custom recovery passphrase. */ + data class UpdateCustomPassphrase(val value: String) : SecureBackupSetupEvents + + /** Update the user-typed confirmation field. */ + data class UpdateCustomPassphraseConfirm(val value: String) : SecureBackupSetupEvents + + /** Advance from the Entry step to the Confirm step. No-op unless the entry passphrase meets requirements. */ + data object ContinueCustomPassphrase : SecureBackupSetupEvents + + /** Step back from Confirm to Entry. Both typed values are preserved. */ + data object BackToCustomEntry : SecureBackupSetupEvents + + /** Submit the custom passphrase to the SDK (only valid when [SecureBackupSetupState.canSubmitCustomPassphrase]). */ + data object SubmitCustomPassphrase : SecureBackupSetupEvents + + /** Abort an in-flight custom submit (back press while creating) and return to the Confirm step. */ + data object CancelCustomPassphraseSubmit : SecureBackupSetupEvents } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt index 9f27bc9566..9145b4a49b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt @@ -11,6 +11,7 @@ package io.element.android.features.securebackup.impl.setup import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -22,22 +23,33 @@ import com.freeletics.flowredux.compose.rememberStateAndDispatch import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.securebackup.impl.loggerTagSetup import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUserStory import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements +import io.element.android.libraries.wellknown.api.SessionWellknownRetriever +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import timber.log.Timber +private data class WellknownStatus( + val loaded: Boolean, + val customRecoveryPassphraseRequirements: CustomRecoveryPassphraseRequirements?, +) + @AssistedInject class SecureBackupSetupPresenter( @Assisted private val isChangeRecoveryKeyUserStory: Boolean, private val stateMachine: SecureBackupSetupStateMachine, private val encryptionService: EncryptionService, + private val sessionWellknownRetriever: SessionWellknownRetriever, + private val enterpriseService: EnterpriseService, ) : Presenter { @AssistedFactory interface Factory { @@ -53,10 +65,81 @@ class SecureBackupSetupPresenter( } var showSaveConfirmationDialog by remember { mutableStateOf(false) } + // Spinner until the well-known fetch settles, so the user can't start the auto-gen path + // while a custom spec is still in flight. The loaded/specs pair flips in one assignment. + val wellknownStatusState = remember { + mutableStateOf(WellknownStatus(loaded = false, customRecoveryPassphraseRequirements = null)) + } + val wellknownLoaded by remember { + derivedStateOf { wellknownStatusState.value.loaded } + } + val customRecoveryPassphraseRequirements by remember { + derivedStateOf { wellknownStatusState.value.customRecoveryPassphraseRequirements } + } + // Not rememberSaveable: the passphrase must never reach the on-disk saved-state bundle. + // Plain String (not a zeroed CharArray): the TextField/event/SDK boundary all copy Strings + // anyway, so we just clear it on success and accept loss on process death. + var customPassphrase by remember { mutableStateOf("") } + var customPassphraseConfirm by remember { mutableStateOf("") } + var customEntryStep by remember { mutableStateOf(CustomEntryStep.Entry) } + // Single-flight guard: a button tap and an IME-Done in the same frame both see + // canSubmit=true and would each launch an enableRecovery coroutine. The state machine + // dedupes UserCreatesKey, but only this flag stops the duplicate SDK call. + var customSubmitInFlight by remember { mutableStateOf(false) } + // Handle to the in-flight custom submit so a back press can abort it (see CancelCustomPassphraseSubmit). + var customSubmitJob by remember { mutableStateOf(null) } + + LaunchedEffect(setupState) { + if (setupState !is SetupState.Creating) { + customSubmitInFlight = false + } + } + + LaunchedEffect(Unit) { + val result = sessionWellknownRetriever.getElementWellKnown() + // Enterprise gate: even if the homeserver advertises a custom spec, only honor it on + // builds where the feature is enabled. FOSS builds always fall back to the auto-gen path. + val specsFromWellknown = (result as? WellknownRetrieverResult.Success)?.data?.customRecoveryPassphraseRequirements + wellknownStatusState.value = WellknownStatus( + loaded = true, + customRecoveryPassphraseRequirements = specsFromWellknown.takeIf { enterpriseService.isCustomRecoveryPassphraseEnabled() }, + ) + } + + // Shared with SecureBackupSetupStateProvider so previews never drift from runtime. + val derivations by remember { + derivedStateOf { + deriveCustomPassphraseState( + requirements = wellknownStatusState.value.customRecoveryPassphraseRequirements, + passphrase = customPassphrase, + confirm = customPassphraseConfirm, + step = customEntryStep, + setupState = setupState, + ) + } + } + + // Strength is an enterprise-only estimation; null while the field is empty or in FOSS builds. + // Only computed on the Entry step, the only place the indicator is rendered. + val customPassphraseStrength by remember { + derivedStateOf { + customPassphrase + .takeIf { customEntryStep == CustomEntryStep.Entry && it.isNotEmpty() } + ?.let { enterpriseService.estimateCustomRecoveryPassphraseStrength(it) } + } + } + + // Nothing to "save" when the user chose the passphrase: auto-skip the Created step. + LaunchedEffect(setupState, customRecoveryPassphraseRequirements) { + if (customRecoveryPassphraseRequirements != null && setupState is SetupState.Created) { + stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserSavedKey) + } + } + fun handleEvent(event: SecureBackupSetupEvents) { when (event) { SecureBackupSetupEvents.CreateRecoveryKey -> { - coroutineScope.createOrChangeRecoveryKey(stateAndDispatch) + coroutineScope.createOrChangeRecoveryKey(stateAndDispatch, passphrase = null) } SecureBackupSetupEvents.RecoveryKeyHasBeenSaved -> stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserSavedKey) @@ -67,12 +150,51 @@ class SecureBackupSetupPresenter( SecureBackupSetupEvents.Done -> { showSaveConfirmationDialog = true } + is SecureBackupSetupEvents.UpdateCustomPassphrase -> { + customPassphrase = event.value + } + is SecureBackupSetupEvents.UpdateCustomPassphraseConfirm -> { + customPassphraseConfirm = event.value + } + SecureBackupSetupEvents.ContinueCustomPassphrase -> { + if (derivations.canContinueFromEntry) { + customEntryStep = CustomEntryStep.Confirm + } + } + SecureBackupSetupEvents.BackToCustomEntry -> { + customEntryStep = CustomEntryStep.Entry + } + SecureBackupSetupEvents.SubmitCustomPassphrase -> { + if (derivations.canSubmitCustomPassphrase && !customSubmitInFlight) { + customSubmitInFlight = true + customSubmitJob = coroutineScope.createOrChangeRecoveryKey( + stateAndDispatch, + passphrase = customPassphrase, + onSuccess = { + customPassphrase = "" + customPassphraseConfirm = "" + }, + ) + } + } + SecureBackupSetupEvents.CancelCustomPassphraseSubmit -> { + // Abort the SDK call and snap back to Initial. Dispatch the reset first so that + // if the (now cancelled) coroutine still manages to dispatch SdkError/ + // SdkHasCreatedKey, those land in Initial where the state machine ignores them. + customSubmitJob?.cancel() + customSubmitJob = null + customSubmitInFlight = false + stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserCancelledCreate) + } } } + val isCustomFlow = customRecoveryPassphraseRequirements != null val recoveryKeyViewState = RecoveryKeyViewState( recoveryKeyUserStory = if (isChangeRecoveryKeyUserStory) RecoveryKeyUserStory.Change else RecoveryKeyUserStory.Setup, - formattedRecoveryKey = setupState.recoveryKey(), + // Custom flow: never surface the SDK base58 key in view state, even during the + // brief Created/CreatedAndSaved auto-skip window. + formattedRecoveryKey = if (isCustomFlow) null else setupState.recoveryKey(), displayTextFieldContents = true, inProgress = setupState is SetupState.Creating, ) @@ -82,6 +204,16 @@ class SecureBackupSetupPresenter( recoveryKeyViewState = recoveryKeyViewState, setupState = setupState, showSaveConfirmationDialog = showSaveConfirmationDialog, + wellknownLoaded = wellknownLoaded, + customRecoveryPassphraseRequirements = customRecoveryPassphraseRequirements, + customEntryStep = customEntryStep, + customPassphrase = customPassphrase, + customPassphraseConfirm = customPassphraseConfirm, + customPassphraseMeetsMinLength = derivations.meetsMinLength, + customPassphraseMismatch = derivations.mismatch, + customPassphraseStrength = customPassphraseStrength, + canContinueFromEntry = derivations.canContinueFromEntry, + canSubmitCustomPassphrase = derivations.canSubmitCustomPassphrase, eventSink = ::handleEvent, ) } @@ -98,47 +230,43 @@ class SecureBackupSetupPresenter( } private fun CoroutineScope.createOrChangeRecoveryKey( - stateAndDispatch: StateAndDispatch + stateAndDispatch: StateAndDispatch, + passphrase: String?, + onSuccess: () -> Unit = {}, ) = launch { stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserCreatesKey) - if (isChangeRecoveryKeyUserStory) { + // Custom passphrase → enableRecovery(passphrase): the SDK derives the 4S key from it. + // For the Change flow this rotates in place — the SDK skips backup creation when recovery + // is already enabled (confirmed), so there's no key-backup teardown or room-key re-upload. + val result = if (passphrase != null) { + Timber.tag(loggerTagSetup.value).d("Calling encryptionService.enableRecovery(passphrase=present)") + encryptionService.enableRecovery(waitForBackupsToUpload = false, passphrase = passphrase) + } else if (isChangeRecoveryKeyUserStory) { Timber.tag(loggerTagSetup.value).d("Calling encryptionService.resetRecoveryKey()") - encryptionService.resetRecoveryKey().fold( - onSuccess = { - stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(it)) - }, - onFailure = { - if (it is Exception) { - stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkError(it)) - } - } - ) + encryptionService.resetRecoveryKey() } else { - observeEncryptionService(stateAndDispatch) - Timber.tag(loggerTagSetup.value).d("Calling encryptionService.enableRecovery()") - encryptionService.enableRecovery(waitForBackupsToUpload = false).onFailure { + Timber.tag(loggerTagSetup.value).d("Calling encryptionService.enableRecovery(passphrase=absent)") + encryptionService.enableRecovery(waitForBackupsToUpload = false, passphrase = null) + } + result.fold( + onSuccess = { key -> + // Clear buffers only on success; on failure keep them so the user can retry + // without retyping. + onSuccess() + // Custom flow: scrub the SDK base58 key from the state machine so SetupState + // never carries it. RustEncryptionService likewise keeps it out of + // enableRecoveryProgressStateFlow for the passphrase path, so it is not retained + // anywhere once this local goes out of scope. + val storedKey = if (passphrase != null) "" else key + stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(storedKey)) + }, + onFailure = { Timber.tag(loggerTagSetup.value).e(it, "Failed to enable recovery") - if (it is Exception) { - stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkError(it)) - } + // The state machine only accepts Exception; wrap anything else so a failure + // still leaves the Creating spinner instead of hanging on it. + val exception = it as? Exception ?: RuntimeException(it) + stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkError(exception)) } - } - } - - private fun CoroutineScope.observeEncryptionService( - stateAndDispatch: StateAndDispatch - ) = launch { - encryptionService.enableRecoveryProgressStateFlow.collect { enableRecoveryProgress -> - Timber.tag(loggerTagSetup.value).d("New enableRecoveryProgress: ${enableRecoveryProgress.javaClass.simpleName}") - when (enableRecoveryProgress) { - is EnableRecoveryProgress.Starting, - is EnableRecoveryProgress.CreatingBackup, - is EnableRecoveryProgress.CreatingRecoveryKey, - is EnableRecoveryProgress.BackingUp, - is EnableRecoveryProgress.RoomKeyUploadError -> Unit - is EnableRecoveryProgress.Done -> - stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.SdkHasCreatedKey(enableRecoveryProgress.recoveryKey)) - } - } + ) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt index 752b5e4851..0e5b60dab7 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupState.kt @@ -8,16 +8,40 @@ package io.element.android.features.securebackup.impl.setup +import io.element.android.features.enterprise.api.CustomRecoveryPassphraseStrengthResult import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState +import io.element.android.libraries.wellknown.api.CustomRecoveryPassphraseRequirements data class SecureBackupSetupState( val isChangeRecoveryKeyUserStory: Boolean, val recoveryKeyViewState: RecoveryKeyViewState, val showSaveConfirmationDialog: Boolean, val setupState: SetupState, + /** False while the well-known is still being fetched; the view shows a spinner until true. */ + val wellknownLoaded: Boolean, + /** Non-null when the well-known requires a user-chosen passphrase; hides the generated-key UI. */ + val customRecoveryPassphraseRequirements: CustomRecoveryPassphraseRequirements?, + val customEntryStep: CustomEntryStep, + /** User-typed passphrase. Not persisted to saved-state; cleared after a successful submit. */ + val customPassphrase: String, + /** Confirmation field; same lifetime contract as [customPassphrase]. */ + val customPassphraseConfirm: String, + /** True when [customPassphrase] satisfies the minimum-character-count rule from [customRecoveryPassphraseRequirements]. */ + val customPassphraseMeetsMinLength: Boolean, + val customPassphraseMismatch: Boolean, + /** Null while the Entry-step passphrase is empty; otherwise the latest strength reading rendered below the field. */ + val customPassphraseStrength: CustomRecoveryPassphraseStrengthResult?, + /** True when the Entry-step passphrase is valid enough to advance to the Confirm step. */ + val canContinueFromEntry: Boolean, + val canSubmitCustomPassphrase: Boolean, val eventSink: (SecureBackupSetupEvents) -> Unit ) +enum class CustomEntryStep { + Entry, + Confirm, +} + sealed interface SetupState { data object Init : SetupState data object Creating : SetupState diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateMachine.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateMachine.kt index 150aeeddc7..62478e3b87 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateMachine.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupStateMachine.kt @@ -34,6 +34,9 @@ class SecureBackupSetupStateMachine : FlowReduxStateMachine -> state.override { State.KeyCreated(event.key) } } + on { _: Event.UserCancelledCreate, state: MachineState -> + state.override { State.Initial } + } } inState { on { _: Event.UserSavedKey, state: MachineState -> @@ -64,5 +67,8 @@ class SecureBackupSetupStateMachine : FlowReduxStateMachine { override val values: Sequence get() = sequenceOf( + aSecureBackupSetupState(wellknownLoaded = false), aSecureBackupSetupState(setupState = SetupState.Init), aSecureBackupSetupState(setupState = SetupState.Creating), aSecureBackupSetupState(setupState = SetupState.Created(aFormattedRecoveryKey())), @@ -25,25 +29,141 @@ open class SecureBackupSetupStateProvider : PreviewParameterProvider Unit, modifier: Modifier = Modifier, ) { + // Custom flow auto-skips the "save your key" screen: finish once the SDK accepted it. + LaunchedEffect(state.setupState, state.isCustomEntry()) { + if (state.isCustomEntry() && state.setupState is SetupState.CreatedAndSaved) { + onSuccess() + } + } FlowStepPage( modifier = modifier, - onBackClick = onBackClick.takeIf { state.canGoBack() }, + onBackClick = backClickHandler(state, onBackClick), title = title(state), subTitle = subtitle(state), iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()), - buttons = { Buttons(state, onFinish = onSuccess) }, + buttons = { Buttons(state, onFinish = onSuccess, onCancel = onBackClick) }, ) { Content(state = state) } @@ -78,15 +120,45 @@ private fun SecureBackupSetupState.canGoBack(): Boolean { return recoveryKeyViewState.formattedRecoveryKey == null } +private fun SecureBackupSetupState.isCustomEntry(): Boolean = + customRecoveryPassphraseRequirements != null + +private fun backClickHandler( + state: SecureBackupSetupState, + onBackClick: () -> Unit, +): (() -> Unit)? { + if (!state.canGoBack()) return null + if (state.isCustomEntry()) { + // Back while the SDK call is in flight aborts it and returns to the Confirm step + // (typed input preserved) rather than silently flipping the step under the spinner. + if (state.setupState is SetupState.Creating) { + return { state.eventSink.invoke(SecureBackupSetupEvents.CancelCustomPassphraseSubmit) } + } + if (state.customEntryStep == CustomEntryStep.Confirm) { + // In the custom flow, backing out of Confirm returns to Entry (preserve typed input). + return { state.eventSink.invoke(SecureBackupSetupEvents.BackToCustomEntry) } + } + } + return onBackClick +} + @Composable private fun title(state: SecureBackupSetupState): String { + // Hide the heading until the well-known resolves, so it can't flip copy under the user. + if (!state.wellknownLoaded) return "" + // Custom flow has no "save your key" step — use per-step custom titles throughout. + if (state.isCustomEntry()) { + return when (state.customEntryStep) { + CustomEntryStep.Entry -> stringResource(id = R.string.screen_recovery_key_custom_title) + CustomEntryStep.Confirm -> stringResource(id = R.string.screen_recovery_key_custom_confirm_title) + } + } return when (state.setupState) { SetupState.Init, SetupState.Creating, - is SetupState.Error -> if (state.isChangeRecoveryKeyUserStory) { - stringResource(id = R.string.screen_recovery_key_change_title) - } else { - stringResource(id = R.string.screen_recovery_key_setup_title) + is SetupState.Error -> when { + state.isChangeRecoveryKeyUserStory -> stringResource(id = R.string.screen_recovery_key_change_title) + else -> stringResource(id = R.string.screen_recovery_key_setup_title) } is SetupState.Created, is SetupState.CreatedAndSaved -> @@ -95,14 +167,20 @@ private fun title(state: SecureBackupSetupState): String { } @Composable -private fun subtitle(state: SecureBackupSetupState): String { +private fun subtitle(state: SecureBackupSetupState): String? { + if (!state.wellknownLoaded) return null + if (state.isCustomEntry()) { + return when (state.customEntryStep) { + CustomEntryStep.Entry -> stringResource(id = R.string.screen_recovery_key_custom_description) + CustomEntryStep.Confirm -> stringResource(id = R.string.screen_recovery_key_custom_confirm_description) + } + } return when (state.setupState) { SetupState.Init, SetupState.Creating, - is SetupState.Error -> if (state.isChangeRecoveryKeyUserStory) { - stringResource(id = R.string.screen_recovery_key_change_description) - } else { - stringResource(id = R.string.screen_recovery_key_setup_description) + is SetupState.Error -> when { + state.isChangeRecoveryKeyUserStory -> stringResource(id = R.string.screen_recovery_key_change_description) + else -> stringResource(id = R.string.screen_recovery_key_setup_description) } is SetupState.Created, is SetupState.CreatedAndSaved -> @@ -114,6 +192,25 @@ private fun subtitle(state: SecureBackupSetupState): String { private fun Content( state: SecureBackupSetupState, ) { + // Spinner until the well-known resolves, so neither flow renders before we know which applies. + if (!state.wellknownLoaded && state.setupState == SetupState.Init) { + LoadingPlaceholder() + return + } + if (state.isCustomEntry()) { + when (state.setupState) { + SetupState.Init, + is SetupState.Error -> when (state.customEntryStep) { + CustomEntryStep.Entry -> CustomPassphraseEntry(state = state) + CustomEntryStep.Confirm -> CustomPassphraseConfirm(state = state) + } + // Hold the spinner through the auto-skip so the SDK base58 key is never shown/shared. + SetupState.Creating, + is SetupState.Created, + is SetupState.CreatedAndSaved -> LoadingPlaceholder() + } + return + } val context = LocalContext.current val formattedRecoveryKey = state.recoveryKeyViewState.formattedRecoveryKey val toastMessage = stringResource(R.string.screen_recovery_key_copied_to_clipboard) @@ -144,10 +241,269 @@ private fun Content( ) } +@Composable +private fun LoadingPlaceholder() { + val description = stringResource(id = R.string.a11y_recovery_key_loading_specs) + Box( + modifier = Modifier + .fillMaxWidth() + .padding(top = 52.dp) + .semantics { contentDescription = description }, + contentAlignment = Alignment.Center, + ) { + // CircularProgressIndicator applies progressSemantics() internally — no need to add it here. + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + strokeWidth = 2.dp, + ) + } +} + +@Composable +private fun CustomPassphraseEntry(state: SecureBackupSetupState) { + val specs = state.customRecoveryPassphraseRequirements ?: return + var passphraseVisible by rememberSaveable { mutableStateOf(false) } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp), + ) { + TextField( + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.customRecoveryPassphrase) + .semantics { contentType = ContentType.NewPassword }, + value = state.customPassphrase, + onValueChange = { state.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(it)) }, + label = stringResource(id = CommonStrings.common_recovery_key), + singleLine = true, + visualTransformation = if (passphraseVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + PasswordVisibilityToggle( + visible = passphraseVisible, + onToggle = { passphraseVisible = !passphraseVisible }, + ) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { + if (state.canContinueFromEntry) { + state.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + } + }, + ), + supportingText = pluralStringResource( + id = R.plurals.screen_recovery_key_custom_requirement_length, + count = specs.minCharacterCount, + specs.minCharacterCount, + ), + ) + // Always render the strength row in the Entry step (matches iOS): an empty field shows the + // "Strength" base label with an empty bar; typing flips it to the per-level label + bar. + Spacer(modifier = Modifier.height(8.dp)) + PassphraseStrengthIndicator( + result = state.customPassphraseStrength, + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Composable +private fun PassphraseStrengthIndicator( + result: CustomRecoveryPassphraseStrengthResult?, + modifier: Modifier = Modifier, +) { + val label = stringResource( + id = result?.strength?.labelRes() ?: R.string.screen_recovery_key_custom_strength_base, + ) + val hint = result?.strength?.hintRes()?.let { stringResource(id = it) } + val score = result?.score ?: 0f + // Mirror iOS: a zero score (empty field or the Garbage tier) stays neutral; otherwise the + // label and bar share a single colour interpolated along the red→orange→yellow→green gradient. + val color = if (score <= 0f) ElementTheme.colors.textSecondary else passphraseStrengthColor(score) + val announcement = stringResource(id = R.string.a11y_recovery_key_custom_strength_announcement, label) + .let { if (hint != null) "$it. $hint" else it } + Column( + modifier = modifier.semantics(mergeDescendants = true) { + contentDescription = announcement + }, + ) { + Text( + text = label, + color = color, + style = ElementTheme.typography.fontBodySmMedium, + ) + Spacer(modifier = Modifier.height(4.dp)) + LinearProgressIndicator( + progress = { score }, + modifier = Modifier.fillMaxWidth(), + color = color, + ) + if (hint != null) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = hint, + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodySmRegular, + ) + } + } +} + +private fun CustomRecoveryPassphraseStrength.labelRes(): Int = when (this) { + CustomRecoveryPassphraseStrength.Garbage -> R.string.screen_recovery_key_custom_strength_garbage + CustomRecoveryPassphraseStrength.Weak -> R.string.screen_recovery_key_custom_strength_weak + CustomRecoveryPassphraseStrength.Moderate -> R.string.screen_recovery_key_custom_strength_moderate + CustomRecoveryPassphraseStrength.Okay -> R.string.screen_recovery_key_custom_strength_okay + CustomRecoveryPassphraseStrength.Strong -> R.string.screen_recovery_key_custom_strength_strong + CustomRecoveryPassphraseStrength.VeryStrong -> R.string.screen_recovery_key_custom_strength_very_strong + CustomRecoveryPassphraseStrength.UltraStrong -> R.string.screen_recovery_key_custom_strength_ultra_strong + CustomRecoveryPassphraseStrength.Mega -> R.string.screen_recovery_key_custom_strength_mega +} + +private fun CustomRecoveryPassphraseStrength.hintRes(): Int = when (this) { + CustomRecoveryPassphraseStrength.Garbage -> R.string.screen_recovery_key_custom_strength_hint_garbage + CustomRecoveryPassphraseStrength.Weak -> R.string.screen_recovery_key_custom_strength_hint_weak + CustomRecoveryPassphraseStrength.Moderate -> R.string.screen_recovery_key_custom_strength_hint_moderate + CustomRecoveryPassphraseStrength.Okay -> R.string.screen_recovery_key_custom_strength_hint_okay + CustomRecoveryPassphraseStrength.Strong -> R.string.screen_recovery_key_custom_strength_hint_strong + CustomRecoveryPassphraseStrength.VeryStrong -> R.string.screen_recovery_key_custom_strength_hint_very_strong + CustomRecoveryPassphraseStrength.UltraStrong -> R.string.screen_recovery_key_custom_strength_hint_ultra_strong + CustomRecoveryPassphraseStrength.Mega -> R.string.screen_recovery_key_custom_strength_hint_mega +} + +// Stops mirror iOS PasswordStrengthBar.gradient: Compound red900 / lime700 with the same vivid +// orange & yellow midpoints. Intentionally fixed (not light/dark semantic) so the gradient reads +// identically to iOS in both themes. +private val passphraseStrengthGradientStops = listOf( + 0.25f to Color(0xFFD51928), // Compound red900 + 0.5f to Color(0xFFFF9500), // orange + 0.75f to Color(0xFFFFCC00), // yellow + 1.0f to Color(0xFF54C424), // Compound lime700 +) + +/** Interpolates the strength bar colour along [passphraseStrengthGradientStops] for a 0f..1f score. */ +private fun passphraseStrengthColor(score: Float): Color { + val clamped = score.coerceIn(0f, 1f) + var previousLocation = 0f + var previousColor = passphraseStrengthGradientStops.first().second + for ((location, color) in passphraseStrengthGradientStops) { + if (clamped <= location) { + val span = location - previousLocation + if (span <= 0f) return color + return lerp(previousColor, color, ((clamped - previousLocation) / span).coerceIn(0f, 1f)) + } + previousLocation = location + previousColor = color + } + return previousColor +} + +@Composable +private fun CustomPassphraseConfirm(state: SecureBackupSetupState) { + var passphraseVisible by rememberSaveable { mutableStateOf(false) } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp), + ) { + TextField( + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.customRecoveryPassphraseConfirm) + .semantics { contentType = ContentType.NewPassword }, + value = state.customPassphraseConfirm, + onValueChange = { state.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(it)) }, + label = stringResource(id = CommonStrings.common_recovery_key), + singleLine = true, + visualTransformation = if (passphraseVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + PasswordVisibilityToggle( + visible = passphraseVisible, + onToggle = { passphraseVisible = !passphraseVisible }, + ) + }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { + if (state.canSubmitCustomPassphrase) { + state.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + } + }, + ), + validity = if (state.customPassphraseMismatch) TextFieldValidity.Invalid else TextFieldValidity.None, + supportingText = if (state.customPassphraseMismatch) { + stringResource(id = R.string.screen_recovery_key_custom_mismatch) + } else { + null + }, + ) + } +} + @Composable private fun ColumnScope.Buttons( state: SecureBackupSetupState, onFinish: () -> Unit, + onCancel: () -> Unit, +) { + // No buttons until the well-known resolves; Content shows a spinner meanwhile. + if (!state.wellknownLoaded && state.setupState == SetupState.Init) return + if (state.isCustomEntry()) { + CustomButtons(state = state, onCancel = onCancel) + } else { + AutoGenButtons(state = state, onFinish = onFinish) + } +} + +@Composable +private fun ColumnScope.CustomButtons( + state: SecureBackupSetupState, + onCancel: () -> Unit, +) { + // No save/share controls in the custom flow — they'd leak the base58 key during Created. + when (state.setupState) { + SetupState.Creating, + is SetupState.Created, + is SetupState.CreatedAndSaved -> return + SetupState.Init, + is SetupState.Error -> Unit + } + when (state.customEntryStep) { + CustomEntryStep.Entry -> { + Button( + text = stringResource(id = CommonStrings.action_continue), + enabled = state.canContinueFromEntry, + modifier = Modifier.fillMaxWidth(), + onClick = { state.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) }, + ) + TextButton( + text = stringResource(id = CommonStrings.action_cancel), + modifier = Modifier.fillMaxWidth(), + onClick = onCancel, + ) + } + CustomEntryStep.Confirm -> { + Button( + text = stringResource(id = R.string.screen_recovery_key_custom_finish_action), + enabled = state.canSubmitCustomPassphrase, + modifier = Modifier.fillMaxWidth(), + onClick = { state.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) }, + ) + } + } +} + +@Composable +private fun ColumnScope.AutoGenButtons( + state: SecureBackupSetupState, + onFinish: () -> Unit, ) { val context = LocalContext.current val chooserTitle = stringResource(id = R.string.screen_recovery_key_save_action) diff --git a/features/securebackup/impl/src/main/res/values/temporary.xml b/features/securebackup/impl/src/main/res/values/temporary.xml new file mode 100644 index 0000000000..3edbd84d73 --- /dev/null +++ b/features/securebackup/impl/src/main/res/values/temporary.xml @@ -0,0 +1,37 @@ + + + Enter a custom recovery key + Choose a recovery key that you can memorize. + Confirm your recovery key + Enter your recovery key again. + The recovery key you entered doesn\'t match + + Minimum %1$d character + Minimum %1$d characters + + Finish setup + Strength + Garbage + Weak + Moderate + Okay + Strong + Very strong + Super strong + Ideal + Cracking would take roughly milliseconds. + Cracking would take roughly minutes to months. + Cracking would take roughly years. + Cracking would take roughly hundreds of years. + Cracking would take roughly billions of years. + Cracking would take roughly trillions of years. + Cracking would take roughly quadrillions of years. + Cracking would take roughly quintillions of years. + Loading recovery key requirements + Passphrase strength: %1$s + diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt index a3cf920d82..6ca716bcd6 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt @@ -6,23 +6,37 @@ * Please see LICENSE files in the repository root for full details. */ +@file:OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) + package io.element.android.features.securebackup.impl.setup import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.enterprise.api.CustomRecoveryPassphraseStrength +import io.element.android.features.enterprise.api.CustomRecoveryPassphraseStrengthResult +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUserStory import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState -import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress +import io.element.android.features.wellknown.test.FakeSessionWellknownRetriever +import io.element.android.features.wellknown.test.aCustomRecoveryPassphraseRequirements +import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.test.A_RECOVERY_KEY import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService +import io.element.android.libraries.wellknown.api.SessionWellknownRetriever +import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +@Suppress("LargeClass") class SecureBackupSetupPresenterTest { @get:Rule val warmUpRule = WarmUpRule() @@ -33,11 +47,16 @@ class SecureBackupSetupPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - assertThat(initialState.isChangeRecoveryKeyUserStory).isFalse() - assertThat(initialState.setupState).isEqualTo(SetupState.Init) - assertThat(initialState.showSaveConfirmationDialog).isFalse() - assertThat(initialState.recoveryKeyViewState).isEqualTo( + val preFetch = awaitItem() + assertThat(preFetch.wellknownLoaded).isFalse() + assertThat(preFetch.setupState).isEqualTo(SetupState.Init) + + val loaded = awaitItem() + assertThat(loaded.wellknownLoaded).isTrue() + assertThat(loaded.isChangeRecoveryKeyUserStory).isFalse() + assertThat(loaded.setupState).isEqualTo(SetupState.Init) + assertThat(loaded.showSaveConfirmationDialog).isFalse() + assertThat(loaded.recoveryKeyViewState).isEqualTo( RecoveryKeyViewState( recoveryKeyUserStory = RecoveryKeyUserStory.Setup, formattedRecoveryKey = null, @@ -50,14 +69,17 @@ class SecureBackupSetupPresenterTest { @Test fun `present - create recovery key and save it`() = runTest { - val encryptionService = FakeEncryptionService() + val encryptionService = FakeEncryptionService( + enableRecoveryLambda = { _, _ -> Result.success(A_RECOVERY_KEY) }, + ) val presenter = createSecureBackupSetupPresenter( encryptionService = encryptionService ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + awaitItem() // pre-fetch + val initialState = awaitItem() // post-fetch, wellknownLoaded=true initialState.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) val creatingState = awaitItem() assertThat(creatingState.setupState).isEqualTo(SetupState.Creating) @@ -69,7 +91,6 @@ class SecureBackupSetupPresenterTest { inProgress = true, ) ) - encryptionService.emitEnableRecoveryProgress(EnableRecoveryProgress.Done(A_RECOVERY_KEY)) val createdState = awaitItem() assertThat(createdState.setupState).isEqualTo(SetupState.Created(A_RECOVERY_KEY)) assertThat(createdState.recoveryKeyViewState).isEqualTo( @@ -100,7 +121,9 @@ class SecureBackupSetupPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + awaitItem() // pre-fetch val initialState = awaitItem() + assertThat(initialState.wellknownLoaded).isTrue() assertThat(initialState.isChangeRecoveryKeyUserStory).isTrue() assertThat(initialState.setupState).isEqualTo(SetupState.Init) assertThat(initialState.recoveryKeyViewState).isEqualTo( @@ -117,7 +140,7 @@ class SecureBackupSetupPresenterTest { @Test fun `present - handle errors`() = runTest { val encryptionService = FakeEncryptionService( - enableRecoveryLambda = { Result.failure(IllegalStateException("Test error")) } + enableRecoveryLambda = { _, _ -> Result.failure(IllegalStateException("Test error")) } ) val presenter = createSecureBackupSetupPresenter( isChangeRecoveryKeyUserStory = false, @@ -126,6 +149,7 @@ class SecureBackupSetupPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + awaitItem() // pre-fetch val initialState = awaitItem() assertThat(initialState.isChangeRecoveryKeyUserStory).isFalse() assertThat(initialState.setupState).isEqualTo(SetupState.Init) @@ -152,6 +176,7 @@ class SecureBackupSetupPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + awaitItem() // pre-fetch val initialState = awaitItem() initialState.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) val creatingState = awaitItem() @@ -186,16 +211,727 @@ class SecureBackupSetupPresenterTest { } } + @Test + fun `present - wellknownLoaded flips false to true exactly once after fetch resolves`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val preFetch = awaitItem() + assertThat(preFetch.wellknownLoaded).isFalse() + assertThat(preFetch.customRecoveryPassphraseRequirements).isNull() + + val loaded = awaitItem() + assertThat(loaded.wellknownLoaded).isTrue() + assertThat(loaded.customRecoveryPassphraseRequirements).isNotNull() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec absent leaves customRecoveryPassphraseRequirements null and uses generated-key path`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = null)) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val preFetch = awaitItem() + assertThat(preFetch.wellknownLoaded).isFalse() + val loaded = awaitItem() + assertThat(loaded.wellknownLoaded).isTrue() + assertThat(loaded.customRecoveryPassphraseRequirements).isNull() + assertThat(loaded.canSubmitCustomPassphrase).isFalse() + loaded.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + awaitItem() // creating + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(null)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec present surfaces validation errors and blocks continue`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial, pre-fetch + val withSpec = awaitItem() + assertThat(withSpec.customRecoveryPassphraseRequirements).isNotNull() + assertThat(withSpec.customEntryStep).isEqualTo(CustomEntryStep.Entry) + assertThat(withSpec.canContinueFromEntry).isFalse() + assertThat(withSpec.canSubmitCustomPassphrase).isFalse() + + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase("abc")) + val afterShortInput = awaitItem() + assertThat(afterShortInput.customPassphrase).isEqualTo("abc") + assertThat(afterShortInput.customPassphraseMeetsMinLength).isFalse() + assertThat(afterShortInput.canContinueFromEntry).isFalse() + + // Continue is a no-op when entry is invalid: step stays Entry. + afterShortInput.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + expectNoEvents() + enableRecoveryLambda.assertions().isNeverCalled() + } + } + + @Test + fun `present - custom spec strength is null while empty and delegates to the enterprise estimator once user types`() = runTest { + // The estimation algorithm lives in the enterprise module; here we only verify the presenter + // suppresses the indicator while the field is empty and otherwise forwards the service result. + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + enterpriseService = FakeEnterpriseService( + isCustomRecoveryPassphraseEnabledResult = true, + estimateCustomRecoveryPassphraseStrengthResult = { passphrase -> + if (passphrase.length > 5) { + CustomRecoveryPassphraseStrengthResult(CustomRecoveryPassphraseStrength.Strong, score = 0.9f) + } else { + CustomRecoveryPassphraseStrengthResult(CustomRecoveryPassphraseStrength.Weak, score = 0.1f) + } + }, + ), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + // Field is empty → indicator suppressed (the estimator is never consulted). + assertThat(withSpec.customPassphraseStrength).isNull() + + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase("abc")) + val afterShort = awaitItem() + assertThat(afterShort.customPassphraseStrength?.strength) + .isEqualTo(CustomRecoveryPassphraseStrength.Weak) + + afterShort.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase("Abcdefg1!@#xyz")) + val afterStrong = awaitItem() + assertThat(afterStrong.customPassphraseStrength?.strength) + .isEqualTo(CustomRecoveryPassphraseStrength.Strong) + + // Clearing the field brings the indicator back to null. + afterStrong.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase("")) + val cleared = awaitItem() + assertThat(cleared.customPassphraseStrength).isNull() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec ContinueCustomPassphrase advances Entry to Confirm when valid`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial + val withSpec = awaitItem() + assertThat(withSpec.customEntryStep).isEqualTo(CustomEntryStep.Entry) + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + val typed = awaitItem() + assertThat(typed.canContinueFromEntry).isTrue() + + typed.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val advanced = awaitItem() + assertThat(advanced.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + assertThat(advanced.customPassphrase).isEqualTo(valid) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec BackToCustomEntry returns to Entry preserving both fields`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + assertThat(onConfirm.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + awaitItem() + + onConfirm.eventSink.invoke(SecureBackupSetupEvents.BackToCustomEntry) + val backOnEntry = awaitItem() + assertThat(backOnEntry.customEntryStep).isEqualTo(CustomEntryStep.Entry) + assertThat(backOnEntry.customPassphrase).isEqualTo(valid) + assertThat(backOnEntry.customPassphraseConfirm).isEqualTo(valid) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec SubmitCustomPassphrase from Entry step is a no-op`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + val typed = awaitItem() + // Both fields contain matching content via a stale path: simulate by also setting confirm + // while still on the Entry step (e.g., previously typed). Submit must still be gated. + typed.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val bothTyped = awaitItem() + assertThat(bothTyped.customEntryStep).isEqualTo(CustomEntryStep.Entry) + assertThat(bothTyped.canSubmitCustomPassphrase).isFalse() + + bothTyped.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + expectNoEvents() + enableRecoveryLambda.assertions().isNeverCalled() + } + } + + @Test + fun `present - custom spec present submit with valid input forwards passphrase to SDK`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + val afterKey = awaitItem() + afterKey.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + assertThat(onConfirm.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + awaitItem() // creating + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(valid)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec error retains typed passphrase and Confirm step across DismissDialog`() = runTest { + val encryptionService = FakeEncryptionService( + enableRecoveryLambda = { _, _ -> Result.failure(IllegalStateException("boom")) }, + ) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + awaitItem() // creating + val errored = awaitItem() + assertThat(errored.setupState).isInstanceOf(SetupState.Error::class.java) + + errored.eventSink.invoke(SecureBackupSetupEvents.DismissDialog) + val afterDismiss = awaitItem() + assertThat(afterDismiss.setupState).isEqualTo(SetupState.Init) + assertThat(afterDismiss.customPassphrase).isEqualTo(valid) + assertThat(afterDismiss.customPassphraseConfirm).isEqualTo(valid) + assertThat(afterDismiss.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + assertThat(afterDismiss.canSubmitCustomPassphrase).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec submit success auto-advances to CreatedAndSaved without manual save`() = runTest { + val encryptionService = FakeEncryptionService( + enableRecoveryLambda = { _, _ -> Result.success(A_RECOVERY_KEY) }, + ) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + awaitItem() // creating + advanceUntilIdle() + + // The presenter's auto-skip LaunchedEffect should have advanced state machine past + // Created without requiring a manual RecoveryKeyHasBeenSaved event. The terminal + // state observed is CreatedAndSaved. + val finalState = expectMostRecentItem() + assertThat(finalState.setupState).isInstanceOf(SetupState.CreatedAndSaved::class.java) + // The SDK-derived base58 key must not leak through the recoveryKeyViewState in + // the custom-passphrase flow. + assertThat(finalState.recoveryKeyViewState.formattedRecoveryKey).isNull() + // The state machine's KeyCreatedAndSaved variant carries a key too — confirm + // the presenter scrubbed it before dispatching SdkHasCreatedKey, so observers + // of setupState don't see the SDK-generated base58 key either. + assertThat(finalState.setupState).isEqualTo(SetupState.CreatedAndSaved(formattedRecoveryKey = "")) + assertThat(finalState.setupState.recoveryKey()).isEqualTo("") + // Typed passphrase is cleared once the SDK call returns successfully. + assertThat(finalState.customPassphrase).isEmpty() + assertThat(finalState.customPassphraseConfirm).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - enterprise feature disabled suppresses spec and forces auto-gen path`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + enterpriseService = FakeEnterpriseService(isCustomRecoveryPassphraseEnabledResult = false), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val loaded = awaitItem() + assertThat(loaded.wellknownLoaded).isTrue() + // Even though the homeserver advertised a spec, the enterprise gate suppresses it. + assertThat(loaded.customRecoveryPassphraseRequirements).isNull() + assertThat(loaded.canSubmitCustomPassphrase).isFalse() + loaded.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + awaitItem() // creating + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(null)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - well-known fetch failure falls back to generated-key path`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Error(IllegalStateException("boom")) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val preFetch = awaitItem() + assertThat(preFetch.wellknownLoaded).isFalse() + val loaded = awaitItem() + assertThat(loaded.wellknownLoaded).isTrue() + assertThat(loaded.customRecoveryPassphraseRequirements).isNull() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - change + custom spec absent uses resetRecoveryKey path`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + isChangeRecoveryKeyUserStory = true, + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = null)) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val loaded = awaitItem() + assertThat(loaded.customRecoveryPassphraseRequirements).isNull() + loaded.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + awaitItem() // creating + val createdState = awaitItem() + assertThat(createdState.setupState).isEqualTo(SetupState.Created(FakeEncryptionService.FAKE_RECOVERY_KEY)) + enableRecoveryLambda.assertions().isNeverCalled() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - change + custom spec present surfaces validation errors and blocks continue`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + isChangeRecoveryKeyUserStory = true, + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial, pre-fetch + val withSpec = awaitItem() + assertThat(withSpec.customRecoveryPassphraseRequirements).isNotNull() + assertThat(withSpec.canContinueFromEntry).isFalse() + + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase("abc")) + val afterShortInput = awaitItem() + assertThat(afterShortInput.customPassphrase).isEqualTo("abc") + assertThat(afterShortInput.customPassphraseMeetsMinLength).isFalse() + assertThat(afterShortInput.canContinueFromEntry).isFalse() + + afterShortInput.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + expectNoEvents() + enableRecoveryLambda.assertions().isNeverCalled() + } + } + + @Test + fun `present - change + custom spec present submit with valid input forwards passphrase to enableRecovery`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + isChangeRecoveryKeyUserStory = true, + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // initial + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + val afterKey = awaitItem() + afterKey.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + awaitItem() // creating + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(valid)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - change + well-known fetch failure falls back to resetRecoveryKey path`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + isChangeRecoveryKeyUserStory = true, + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Error(IllegalStateException("boom")) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val loaded = awaitItem() + assertThat(loaded.customRecoveryPassphraseRequirements).isNull() + loaded.eventSink.invoke(SecureBackupSetupEvents.CreateRecoveryKey) + awaitItem() // creating + val createdState = awaitItem() + assertThat(createdState.setupState).isEqualTo(SetupState.Created(FakeEncryptionService.FAKE_RECOVERY_KEY)) + enableRecoveryLambda.assertions().isNeverCalled() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec Confirm mismatch clears once user edits to match`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + assertThat(onConfirm.customPassphraseMismatch).isFalse() + + // Typing a different value flips mismatch=true and blocks submit. + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm("differen")) + val mismatched = awaitItem() + assertThat(mismatched.customPassphraseMismatch).isTrue() + assertThat(mismatched.canSubmitCustomPassphrase).isFalse() + + // Editing the confirm field to match clears the mismatch and re-enables submit. + mismatched.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val matched = awaitItem() + assertThat(matched.customPassphraseMismatch).isFalse() + assertThat(matched.canSubmitCustomPassphrase).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec rapid double-submit invokes the SDK only once`() = runTest { + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + // Two synchronous submits dispatched from the same snapshot (e.g., button + IME-Done + // racing). The presenter's in-flight guard must drop the second one before it + // launches a second enableRecovery coroutine. + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(valid)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - change + custom spec submit routes to enableRecovery and never resetRecoveryKey`() = runTest { + // Guards the custom-passphrase Change path against silently dropping the passphrase: + // the user's passphrase reaches the SDK only via enableRecovery(passphrase); the + // passphrase-less resetRecoveryKey() path must never be taken here. A true end-to-end + // check that the 4S key actually rotates needs the real SDK + homeserver and lives + // outside this JVM suite. + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val resetRecoveryKeyLambda = lambdaRecorder> { Result.success(FakeEncryptionService.FAKE_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService( + enableRecoveryLambda = enableRecoveryLambda, + resetRecoveryKeyLambda = resetRecoveryKeyLambda, + ) + val presenter = createSecureBackupSetupPresenter( + isChangeRecoveryKeyUserStory = true, + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + awaitItem() // creating + advanceUntilIdle() + enableRecoveryLambda.assertions().isCalledOnce() + .with(value(false), value(valid)) + resetRecoveryKeyLambda.assertions().isNeverCalled() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - custom spec cancel while creating aborts the SDK call and returns to Confirm with input preserved`() = runTest { + // simulateLongTask suspends on delay(1) before invoking the lambda, so cancelling before + // advancing virtual time guarantees the SDK call never runs. + val enableRecoveryLambda = lambdaRecorder> { _, _ -> Result.success(A_RECOVERY_KEY) } + val encryptionService = FakeEncryptionService(enableRecoveryLambda = enableRecoveryLambda) + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + awaitItem() + withSpec.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + onConfirm.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphraseConfirm(valid)) + val ready = awaitItem() + assertThat(ready.canSubmitCustomPassphrase).isTrue() + + ready.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) + val creating = awaitItem() + assertThat(creating.setupState).isEqualTo(SetupState.Creating) + + creating.eventSink.invoke(SecureBackupSetupEvents.CancelCustomPassphraseSubmit) + val cancelled = awaitItem() + assertThat(cancelled.setupState).isEqualTo(SetupState.Init) + assertThat(cancelled.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + assertThat(cancelled.customPassphrase).isEqualTo(valid) + assertThat(cancelled.customPassphraseConfirm).isEqualTo(valid) + assertThat(cancelled.canSubmitCustomPassphrase).isTrue() + + // The cancelled coroutine never reached the SDK, and no zombie success/error followed. + advanceUntilIdle() + enableRecoveryLambda.assertions().isNeverCalled() + expectNoEvents() + } + } + + @Test + fun `present - custom spec strength indicator is suppressed on the Confirm step`() = runTest { + val presenter = createSecureBackupSetupPresenter( + sessionWellknownRetriever = FakeSessionWellknownRetriever { + WellknownRetrieverResult.Success(anElementWellKnown(customRecoveryPassphraseRequirements = aCustomRecoveryPassphraseRequirements())) + }, + enterpriseService = FakeEnterpriseService( + isCustomRecoveryPassphraseEnabledResult = true, + estimateCustomRecoveryPassphraseStrengthResult = { + CustomRecoveryPassphraseStrengthResult(CustomRecoveryPassphraseStrength.Strong, score = 0.9f) + }, + ), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem() // pre-fetch + val withSpec = awaitItem() + + val valid = "Ab12!@cd" + withSpec.eventSink.invoke(SecureBackupSetupEvents.UpdateCustomPassphrase(valid)) + val onEntry = awaitItem() + // Entry step: the estimator result is surfaced. + assertThat(onEntry.customPassphraseStrength?.strength).isEqualTo(CustomRecoveryPassphraseStrength.Strong) + + onEntry.eventSink.invoke(SecureBackupSetupEvents.ContinueCustomPassphrase) + val onConfirm = awaitItem() + // Confirm step: indicator is not rendered, so the presenter stops computing it. + assertThat(onConfirm.customEntryStep).isEqualTo(CustomEntryStep.Confirm) + assertThat(onConfirm.customPassphraseStrength).isNull() + cancelAndIgnoreRemainingEvents() + } + } + private fun createSecureBackupSetupPresenter( isChangeRecoveryKeyUserStory: Boolean = false, encryptionService: EncryptionService = FakeEncryptionService( - enableRecoveryLambda = { Result.success(Unit) }, + enableRecoveryLambda = { _, _ -> Result.success(A_RECOVERY_KEY) }, ), + sessionWellknownRetriever: SessionWellknownRetriever = FakeSessionWellknownRetriever(), + enterpriseService: EnterpriseService = FakeEnterpriseService(isCustomRecoveryPassphraseEnabledResult = true), ): SecureBackupSetupPresenter { return SecureBackupSetupPresenter( isChangeRecoveryKeyUserStory = isChangeRecoveryKeyUserStory, stateMachine = SecureBackupSetupStateMachine(), encryptionService = encryptionService, + sessionWellknownRetriever = sessionWellknownRetriever, + enterpriseService = enterpriseService, ) } } diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 4b3a0d6ffa..593560936e 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -29,6 +29,8 @@ object TestTags { * Verification screen. */ val recoveryKey = TestTag("verification-recovery_key") + val customRecoveryPassphrase = TestTag("verification-custom_recovery_passphrase") + val customRecoveryPassphraseConfirm = TestTag("verification-custom_recovery_passphrase_confirm") /** * Sign out screen. From a6ca2105584ae6e4b769f37a69cfd244d44fe985 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jun 2026 21:23:21 +0000 Subject: [PATCH 319/471] Update dependency com.posthog:posthog-android to v3.47.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ed618ee0e..aeb1bce8a7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -226,7 +226,7 @@ haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = color_picker = "io.mhssn:colorpicker:1.0.0" # Analytics -posthog = "com.posthog:posthog-android:3.43.0" +posthog = "com.posthog:posthog-android:3.47.0" sentry = "io.sentry:sentry-android:8.41.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.33.2" From 3fc48787b80393cab46319eaf05cc4a277d72238 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Tue, 2 Jun 2026 15:01:17 -0700 Subject: [PATCH 320/471] Add missing PasswordVisibilityToggle extraction areas --- .../logout/impl/AccountDeactivationView.kt | 14 +++++--------- .../impl/setup/views/RecoveryKeyView.kt | 16 +++++----------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt index 352efdfb92..c942d2df72 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt @@ -8,7 +8,6 @@ package io.element.android.features.logout.impl -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -60,6 +59,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.PasswordVisibilityToggle 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.TextField @@ -280,14 +280,10 @@ private fun Content( placeholder = stringResource(CommonStrings.common_password), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), trailingIcon = { - val image = - if (passwordVisible) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff() - val description = - if (passwordVisible) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) - - Box(modifier = Modifier.clickable { passwordVisible = !passwordVisible }) { - Icon(imageVector = image, description) - } + PasswordVisibilityToggle( + visible = passwordVisible, + onToggle = { passwordVisible = !passwordVisible }, + ) }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, 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 198891473c..8055709f20 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 @@ -9,7 +9,6 @@ package io.element.android.features.securebackup.impl.setup.views import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -50,6 +49,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.PasswordVisibilityToggle import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.testtags.TestTags @@ -223,16 +223,10 @@ private fun RecoveryKeyFormContent( ), placeholder = stringResource(id = R.string.screen_recovery_key_confirm_key_placeholder), trailingIcon = { - val image = - if (state.displayTextFieldContents) CompoundIcons.VisibilityOn() else CompoundIcons.VisibilityOff() - val description = - if (state.displayTextFieldContents) stringResource(CommonStrings.a11y_hide_password) else stringResource(CommonStrings.a11y_show_password) - Box(Modifier.clickable { toggleRecoveryKeyVisibility(!state.displayTextFieldContents) }) { - Icon( - imageVector = image, - contentDescription = description, - ) - } + PasswordVisibilityToggle( + visible = state.displayTextFieldContents, + onToggle = { toggleRecoveryKeyVisibility(!state.displayTextFieldContents) }, + ) }, ) } From ea529261da915bd7d51187d6f1d90247558a46a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 09:23:40 +0200 Subject: [PATCH 321/471] Update kotlin to v2.3.9 (#6946) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ed618ee0e..f0123733fc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ android_gradle_plugin = "8.13.2" # When updating this, please also update the version in the file ./idea/kotlinc.xml kotlin = "2.3.21" kotlinpoet = "2.3.0" -ksp = "2.3.8" +ksp = "2.3.9" firebaseAppDistribution = "5.2.1" # AndroidX From 1a73ab00941426e2c91ff65058456210729e3f13 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2026 09:58:00 +0200 Subject: [PATCH 322/471] Import compound token from release 10.2.1 https://github.com/element-hq/compound-design-tokens/releases/tag/v10.2.1 (#6942) * Sync compound tokens https://github.com/element-hq/compound-design-tokens/releases/tag/v10.2.1 * Update screenshots --------- Co-authored-by: ElementBot --- .../screenshots/Compound Icons - Dark.png | 4 +- .../screenshots/Compound Icons - Light.png | 4 +- .../screenshots/Compound Icons - Rtl.png | 4 +- .../Compound Vector Icons - Dark.png | 4 +- .../Compound Vector Icons - Light.png | 4 +- .../compound/src/main/assets/theme.iife.js | 2 +- .../tokens/generated/CompoundIcons.kt | 45 +++++++++++++++++++ .../tokens/generated/SemanticColors.kt | 2 + .../tokens/generated/SemanticColorsDark.kt | 1 + .../tokens/generated/SemanticColorsDarkHc.kt | 1 + .../tokens/generated/SemanticColorsLight.kt | 1 + .../tokens/generated/SemanticColorsLightHc.kt | 1 + .../res/drawable/ic_compound_collapse_all.xml | 9 ++++ .../main/res/drawable/ic_compound_crop.xml | 12 +++++ .../res/drawable/ic_compound_expand_all.xml | 9 ++++ .../drawable/ic_compound_flip_horizontal.xml | 9 ++++ .../drawable/ic_compound_flip_vertical.xml | 9 ++++ .../main/res/drawable/ic_compound_save.xml | 9 ++++ .../res/drawable/ic_compound_save_solid.xml | 9 ++++ .../main/res/drawable/ic_compound_unsave.xml | 13 ++++++ ...tem.theme.components_AllIcons_Icons_en.png | 4 +- 21 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_collapse_all.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_crop.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_expand_all.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_flip_horizontal.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_flip_vertical.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_save.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_save_solid.xml create mode 100644 libraries/compound/src/main/res/drawable/ic_compound_unsave.xml diff --git a/libraries/compound/screenshots/Compound Icons - Dark.png b/libraries/compound/screenshots/Compound Icons - Dark.png index 2e3fd0d04d..1209b824be 100644 --- a/libraries/compound/screenshots/Compound Icons - Dark.png +++ b/libraries/compound/screenshots/Compound Icons - Dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7901fea2f578c8ed796160c9c08f417c61f4fb21580f958844fdf0cb794adf8a -size 239731 +oid sha256:f7511b9256afc6d001b53576959e69549683ab4cb81b49031aeda027d14178aa +size 245703 diff --git a/libraries/compound/screenshots/Compound Icons - Light.png b/libraries/compound/screenshots/Compound Icons - Light.png index 6e1bfc80cd..58ba1591ed 100644 --- a/libraries/compound/screenshots/Compound Icons - Light.png +++ b/libraries/compound/screenshots/Compound Icons - Light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:245f012d419817f6557d92a71729b3b70092f24f0eba37f2f1fc431ad27592be -size 252969 +oid sha256:83a12b138543be1e17da8aa562bd95d1de18c0093da50d9a14196f905584bf7c +size 258948 diff --git a/libraries/compound/screenshots/Compound Icons - Rtl.png b/libraries/compound/screenshots/Compound Icons - Rtl.png index aa1b5d93a7..06daf04971 100644 --- a/libraries/compound/screenshots/Compound Icons - Rtl.png +++ b/libraries/compound/screenshots/Compound Icons - Rtl.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d15c52b21cc279d306fa187cd0c318820109b5ec66270e6447e1b02e800eeba -size 254206 +oid sha256:73b37a7cd34f2ffe285011f67c2b38ed5399cbea40b3115c6c5115536f2902f2 +size 260171 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Dark.png b/libraries/compound/screenshots/Compound Vector Icons - Dark.png index a01fbf18b4..5c95b36a89 100644 --- a/libraries/compound/screenshots/Compound Vector Icons - Dark.png +++ b/libraries/compound/screenshots/Compound Vector Icons - Dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85ef188fa3a27e42f4beafc899c1f3e7e8bcfad980ed76af6a03f76d70d6a511 -size 93807 +oid sha256:49a9372bea521e4ee948bbd4f7616310c411ee46f84ffd562d9b35bc9bfeb4d9 +size 97239 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Light.png b/libraries/compound/screenshots/Compound Vector Icons - Light.png index e227f1579c..38764691f4 100644 --- a/libraries/compound/screenshots/Compound Vector Icons - Light.png +++ b/libraries/compound/screenshots/Compound Vector Icons - Light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6e38386e95dc0c50384f06fca122ce14851ceff8ffc7865e394c1b4fccc5db6 -size 100555 +oid sha256:add37e4d84f03a33caf1079eaa99631682f5ef6418096629f96433520e519b20 +size 103742 diff --git a/libraries/compound/src/main/assets/theme.iife.js b/libraries/compound/src/main/assets/theme.iife.js index e7205e4f81..02fd99637a 100644 --- a/libraries/compound/src/main/assets/theme.iife.js +++ b/libraries/compound/src/main/assets/theme.iife.js @@ -1 +1 @@ -var CompoundTheme=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;lt[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),{min:u,max:d}=Math,f=(e,t=0,n=1)=>u(d(t,e),n),p=e=>{e._clipped=!1,e._unclipped=e.slice(0);for(let t=0;t<=3;t++)t<3?((e[t]<0||e[t]>255)&&(e._clipped=!0),e[t]=f(e[t],0,255)):t===3&&(e[t]=f(e[t],0,1));return e},m={};for(let e of[`Boolean`,`Number`,`String`,`Function`,`Array`,`Date`,`RegExp`,`Undefined`,`Null`])m[`[object ${e}]`]=e.toLowerCase();function h(e){return m[Object.prototype.toString.call(e)]||`object`}var g=(e,t=null)=>e.length>=3?Array.prototype.slice.call(e):h(e[0])==`object`&&t?t.split(``).filter(t=>e[0][t]!==void 0).map(t=>e[0][t]):e[0].slice(0),_=e=>{if(e.length<2)return null;let t=e.length-1;return h(e[t])==`string`?e[t].toLowerCase():null},{PI:v,min:y,max:b}=Math,x=e=>Math.round(e*100)/100,S=e=>Math.round(e*100)/100,C=v*2,w=v/3,T=v/180,ee=180/v;function E(e){return[...e.slice(0,3).reverse(),...e.slice(3)]}var D={format:{},autodetect:[]},O=class{constructor(...e){let t=this;if(h(e[0])===`object`&&e[0].constructor&&e[0].constructor===this.constructor)return e[0];let n=_(e),r=!1;if(!n){r=!0,D.sorted||=(D.autodetect=D.autodetect.sort((e,t)=>t.p-e.p),!0);for(let t of D.autodetect)if(n=t.test(...e),n)break}if(D.format[n])t._rgb=p(D.format[n].apply(null,r?e:e.slice(0,-1)));else throw Error(`unknown format: `+e);t._rgb.length===3&&t._rgb.push(1)}toString(){return h(this.hex)==`function`?this.hex():`[${this._rgb.join(`,`)}]`}},te=`3.2.0`,k=(...e)=>new O(...e);k.version=te;var A={aliceblue:`#f0f8ff`,antiquewhite:`#faebd7`,aqua:`#00ffff`,aquamarine:`#7fffd4`,azure:`#f0ffff`,beige:`#f5f5dc`,bisque:`#ffe4c4`,black:`#000000`,blanchedalmond:`#ffebcd`,blue:`#0000ff`,blueviolet:`#8a2be2`,brown:`#a52a2a`,burlywood:`#deb887`,cadetblue:`#5f9ea0`,chartreuse:`#7fff00`,chocolate:`#d2691e`,coral:`#ff7f50`,cornflowerblue:`#6495ed`,cornsilk:`#fff8dc`,crimson:`#dc143c`,cyan:`#00ffff`,darkblue:`#00008b`,darkcyan:`#008b8b`,darkgoldenrod:`#b8860b`,darkgray:`#a9a9a9`,darkgreen:`#006400`,darkgrey:`#a9a9a9`,darkkhaki:`#bdb76b`,darkmagenta:`#8b008b`,darkolivegreen:`#556b2f`,darkorange:`#ff8c00`,darkorchid:`#9932cc`,darkred:`#8b0000`,darksalmon:`#e9967a`,darkseagreen:`#8fbc8f`,darkslateblue:`#483d8b`,darkslategray:`#2f4f4f`,darkslategrey:`#2f4f4f`,darkturquoise:`#00ced1`,darkviolet:`#9400d3`,deeppink:`#ff1493`,deepskyblue:`#00bfff`,dimgray:`#696969`,dimgrey:`#696969`,dodgerblue:`#1e90ff`,firebrick:`#b22222`,floralwhite:`#fffaf0`,forestgreen:`#228b22`,fuchsia:`#ff00ff`,gainsboro:`#dcdcdc`,ghostwhite:`#f8f8ff`,gold:`#ffd700`,goldenrod:`#daa520`,gray:`#808080`,green:`#008000`,greenyellow:`#adff2f`,grey:`#808080`,honeydew:`#f0fff0`,hotpink:`#ff69b4`,indianred:`#cd5c5c`,indigo:`#4b0082`,ivory:`#fffff0`,khaki:`#f0e68c`,laserlemon:`#ffff54`,lavender:`#e6e6fa`,lavenderblush:`#fff0f5`,lawngreen:`#7cfc00`,lemonchiffon:`#fffacd`,lightblue:`#add8e6`,lightcoral:`#f08080`,lightcyan:`#e0ffff`,lightgoldenrod:`#fafad2`,lightgoldenrodyellow:`#fafad2`,lightgray:`#d3d3d3`,lightgreen:`#90ee90`,lightgrey:`#d3d3d3`,lightpink:`#ffb6c1`,lightsalmon:`#ffa07a`,lightseagreen:`#20b2aa`,lightskyblue:`#87cefa`,lightslategray:`#778899`,lightslategrey:`#778899`,lightsteelblue:`#b0c4de`,lightyellow:`#ffffe0`,lime:`#00ff00`,limegreen:`#32cd32`,linen:`#faf0e6`,magenta:`#ff00ff`,maroon:`#800000`,maroon2:`#7f0000`,maroon3:`#b03060`,mediumaquamarine:`#66cdaa`,mediumblue:`#0000cd`,mediumorchid:`#ba55d3`,mediumpurple:`#9370db`,mediumseagreen:`#3cb371`,mediumslateblue:`#7b68ee`,mediumspringgreen:`#00fa9a`,mediumturquoise:`#48d1cc`,mediumvioletred:`#c71585`,midnightblue:`#191970`,mintcream:`#f5fffa`,mistyrose:`#ffe4e1`,moccasin:`#ffe4b5`,navajowhite:`#ffdead`,navy:`#000080`,oldlace:`#fdf5e6`,olive:`#808000`,olivedrab:`#6b8e23`,orange:`#ffa500`,orangered:`#ff4500`,orchid:`#da70d6`,palegoldenrod:`#eee8aa`,palegreen:`#98fb98`,paleturquoise:`#afeeee`,palevioletred:`#db7093`,papayawhip:`#ffefd5`,peachpuff:`#ffdab9`,peru:`#cd853f`,pink:`#ffc0cb`,plum:`#dda0dd`,powderblue:`#b0e0e6`,purple:`#800080`,purple2:`#7f007f`,purple3:`#a020f0`,rebeccapurple:`#663399`,red:`#ff0000`,rosybrown:`#bc8f8f`,royalblue:`#4169e1`,saddlebrown:`#8b4513`,salmon:`#fa8072`,sandybrown:`#f4a460`,seagreen:`#2e8b57`,seashell:`#fff5ee`,sienna:`#a0522d`,silver:`#c0c0c0`,skyblue:`#87ceeb`,slateblue:`#6a5acd`,slategray:`#708090`,slategrey:`#708090`,snow:`#fffafa`,springgreen:`#00ff7f`,steelblue:`#4682b4`,tan:`#d2b48c`,teal:`#008080`,thistle:`#d8bfd8`,tomato:`#ff6347`,turquoise:`#40e0d0`,violet:`#ee82ee`,wheat:`#f5deb3`,white:`#ffffff`,whitesmoke:`#f5f5f5`,yellow:`#ffff00`,yellowgreen:`#9acd32`},ne=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,re=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,ie=e=>{if(e.match(ne)){(e.length===4||e.length===7)&&(e=e.substr(1)),e.length===3&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let t=parseInt(e,16);return[t>>16,t>>8&255,t&255,1]}if(e.match(re)){(e.length===5||e.length===9)&&(e=e.substr(1)),e.length===4&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]+e[3]+e[3]);let t=parseInt(e,16);return[t>>24&255,t>>16&255,t>>8&255,Math.round((t&255)/255*100)/100]}throw Error(`unknown hex color: ${e}`)},{round:ae}=Math,oe=(...e)=>{let[t,n,r,i]=g(e,`rgba`),a=_(e)||`auto`;i===void 0&&(i=1),a===`auto`&&(a=i<1?`rgba`:`rgb`),t=ae(t),n=ae(n),r=ae(r);let o=`000000`+(t<<16|n<<8|r).toString(16);o=o.substr(o.length-6);let s=`0`+ae(i*255).toString(16);switch(s=s.substr(s.length-2),a.toLowerCase()){case`rgba`:return`#${o}${s}`;case`argb`:return`#${s}${o}`;default:return`#${o}`}};O.prototype.name=function(){let e=oe(this._rgb,`rgb`);for(let t of Object.keys(A))if(A[t]===e)return t.toLowerCase();return e},D.format.named=e=>{if(e=e.toLowerCase(),A[e])return ie(A[e]);throw Error(`unknown color name: `+e)},D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&A[e.toLowerCase()])return`named`}}),O.prototype.alpha=function(e,t=!1){return e!==void 0&&h(e)===`number`?t?(this._rgb[3]=e,this):new O([this._rgb[0],this._rgb[1],this._rgb[2],e],`rgb`):this._rgb[3]},O.prototype.clipped=function(){return this._rgb._clipped||!1};var j={Kn:18,labWhitePoint:`d65`,Xn:.95047,Yn:1,Zn:1.08883,t0:.137931034,t1:.206896552,t2:.12841855,t3:.008856452,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},se=new Map([[`a`,[1.0985,.35585]],[`b`,[1.0985,.35585]],[`c`,[.98074,1.18232]],[`d50`,[.96422,.82521]],[`d55`,[.95682,.92149]],[`d65`,[.95047,1.08883]],[`e`,[1,1,1]],[`f2`,[.99186,.67393]],[`f7`,[.95041,1.08747]],[`f11`,[1.00962,.6435]],[`icc`,[.96422,.82521]]]);function M(e){let t=se.get(String(e).toLowerCase());if(!t)throw Error(`unknown Lab illuminant `+e);j.labWhitePoint=e,j.Xn=t[0],j.Zn=t[1]}function ce(){return j.labWhitePoint}var N=(...e)=>{e=g(e,`lab`);let[t,n,r]=e,[i,a,o]=le(t,n,r),[s,c,l]=de(i,a,o);return[s,c,l,e.length>3?e[3]:1]},le=(e,t,n)=>{let{kE:r,kK:i,kKE:a,Xn:o,Yn:s,Zn:c}=j,l=(e+16)/116,u=.002*t+l,d=l-.005*n,f=u*u*u,p=d*d*d,m=f>r?f:(116*u-16)/i,h=e>a?((e+16)/116)**3:e/i,g=p>r?p:(116*d-16)/i;return[m*o,h*s,g*c]},ue=e=>{let t=Math.sign(e);return e=Math.abs(e),(e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055)*t},de=(e,t,n)=>{let{MtxAdaptMa:r,MtxAdaptMaI:i,MtxXYZ2RGB:a,RefWhiteRGB:o,Xn:s,Yn:c,Zn:l}=j,u=s*r.m00+c*r.m10+l*r.m20,d=s*r.m01+c*r.m11+l*r.m21,f=s*r.m02+c*r.m12+l*r.m22,p=o.X*r.m00+o.Y*r.m10+o.Z*r.m20,m=o.X*r.m01+o.Y*r.m11+o.Z*r.m21,h=o.X*r.m02+o.Y*r.m12+o.Z*r.m22,g=(e*r.m00+t*r.m10+n*r.m20)*(p/u),_=(e*r.m01+t*r.m11+n*r.m21)*(m/d),v=(e*r.m02+t*r.m12+n*r.m22)*(h/f),y=g*i.m00+_*i.m10+v*i.m20,b=g*i.m01+_*i.m11+v*i.m21,x=g*i.m02+_*i.m12+v*i.m22,S=ue(y*a.m00+b*a.m10+x*a.m20),C=ue(y*a.m01+b*a.m11+x*a.m21),w=ue(y*a.m02+b*a.m12+x*a.m22);return[S*255,C*255,w*255]},fe=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=he(t,n,r),[c,l,u]=pe(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function pe(e,t,n){let{Xn:r,Yn:i,Zn:a,kE:o,kK:s}=j,c=e/r,l=t/i,u=n/a,d=c>o?c**(1/3):(s*c+16)/116,f=l>o?l**(1/3):(s*l+16)/116,p=u>o?u**(1/3):(s*u+16)/116;return[116*f-16,500*(d-f),200*(f-p)]}function me(e){let t=Math.sign(e);return e=Math.abs(e),(e<=.04045?e/12.92:((e+.055)/1.055)**2.4)*t}var he=(e,t,n)=>{e=me(e/255),t=me(t/255),n=me(n/255);let{MtxRGB2XYZ:r,MtxAdaptMa:i,MtxAdaptMaI:a,Xn:o,Yn:s,Zn:c,As:l,Bs:u,Cs:d}=j,f=e*r.m00+t*r.m10+n*r.m20,p=e*r.m01+t*r.m11+n*r.m21,m=e*r.m02+t*r.m12+n*r.m22,h=o*i.m00+s*i.m10+c*i.m20,g=o*i.m01+s*i.m11+c*i.m21,_=o*i.m02+s*i.m12+c*i.m22,v=f*i.m00+p*i.m10+m*i.m20,y=f*i.m01+p*i.m11+m*i.m21,b=f*i.m02+p*i.m12+m*i.m22;return v*=h/l,y*=g/u,b*=_/d,f=v*a.m00+y*a.m10+b*a.m20,p=v*a.m01+y*a.m11+b*a.m21,m=v*a.m02+y*a.m12+b*a.m22,[f,p,m]};O.prototype.lab=function(){return fe(this._rgb)},Object.assign(k,{lab:(...e)=>new O(...e,`lab`),getLabWhitePoint:ce,setLabWhitePoint:M}),D.format.lab=N,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`lab`),h(e)===`array`&&e.length===3)return`lab`}}),O.prototype.darken=function(e=1){let t=this,n=t.lab();return n[0]-=j.Kn*e,new O(n,`lab`).alpha(t.alpha(),!0)},O.prototype.brighten=function(e=1){return this.darken(-e)},O.prototype.darker=O.prototype.darken,O.prototype.brighter=O.prototype.brighten,O.prototype.get=function(e){let[t,n]=e.split(`.`),r=this[t]();if(n){let e=t.indexOf(n)-(t.substr(0,2)===`ok`?2:0);if(e>-1)return r[e];throw Error(`unknown channel ${n} in mode ${t}`)}else return r};var{pow:ge}=Math,_e=1e-7,ve=20;O.prototype.luminance=function(e,t=`rgb`){if(e!==void 0&&h(e)===`number`){if(e===0)return new O([0,0,0,this._rgb[3]],`rgb`);if(e===1)return new O([255,255,255,this._rgb[3]],`rgb`);let n=this.luminance(),r=ve,i=(n,a)=>{let o=n.interpolate(a,.5,t),s=o.luminance();return Math.abs(e-s)<_e||!r--?o:s>e?i(n,o):i(o,a)};return new O([...(n>e?i(new O([0,0,0]),this):i(this,new O([255,255,255]))).rgb(),this._rgb[3]])}return ye(...this._rgb.slice(0,3))};var ye=(e,t,n)=>(e=be(e),t=be(t),n=be(n),.2126*e+.7152*t+.0722*n),be=e=>(e/=255,e<=.03928?e/12.92:ge((e+.055)/1.055,2.4)),P={},F=(e,t,n=.5,...r)=>{let i=r[0]||`lrgb`;if(!P[i]&&!r.length&&(i=Object.keys(P)[0]),!P[i])throw Error(`interpolation mode ${i} is not defined`);return h(e)!==`object`&&(e=new O(e)),h(t)!==`object`&&(t=new O(t)),P[i](e,t,n).alpha(e.alpha()+n*(t.alpha()-e.alpha()))};O.prototype.mix=O.prototype.interpolate=function(e,t=.5,...n){return F(this,e,t,...n)},O.prototype.premultiply=function(e=!1){let t=this._rgb,n=t[3];return e?(this._rgb=[t[0]*n,t[1]*n,t[2]*n,n],this):new O([t[0]*n,t[1]*n,t[2]*n,n],`rgb`)};var{sin:xe,cos:Se}=Math,Ce=(...e)=>{let[t,n,r]=g(e,`lch`);return isNaN(r)&&(r=0),r*=T,[t,Se(r)*n,xe(r)*n]},we=(...e)=>{e=g(e,`lch`);let[t,n,r]=e,[i,a,o]=Ce(t,n,r),[s,c,l]=N(i,a,o);return[s,c,l,e.length>3?e[3]:1]},Te=(...e)=>we(...E(g(e,`hcl`))),{sqrt:Ee,atan2:De,round:Oe}=Math,ke=(...e)=>{let[t,n,r]=g(e,`lab`),i=Ee(n*n+r*r),a=(De(r,n)*ee+360)%360;return Oe(i*1e4)===0&&(a=NaN),[t,i,a]},Ae=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=fe(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};O.prototype.lch=function(){return Ae(this._rgb)},O.prototype.hcl=function(){return E(Ae(this._rgb))},Object.assign(k,{lch:(...e)=>new O(...e,`lch`),hcl:(...e)=>new O(...e,`hcl`)}),D.format.lch=we,D.format.hcl=Te,[`lch`,`hcl`].forEach(e=>D.autodetect.push({p:2,test:(...t)=>{if(t=g(t,e),h(t)===`array`&&t.length===3)return e}})),O.prototype.saturate=function(e=1){let t=this,n=t.lch();return n[1]+=j.Kn*e,n[1]<0&&(n[1]=0),new O(n,`lch`).alpha(t.alpha(),!0)},O.prototype.desaturate=function(e=1){return this.saturate(-e)},O.prototype.set=function(e,t,n=!1){let[r,i]=e.split(`.`),a=this[r]();if(i){let e=r.indexOf(i)-(r.substr(0,2)===`ok`?2:0);if(e>-1){if(h(t)==`string`)switch(t.charAt(0)){case`+`:a[e]+=+t;break;case`-`:a[e]+=+t;break;case`*`:a[e]*=+t.substr(1);break;case`/`:a[e]/=+t.substr(1);break;default:a[e]=+t}else if(h(t)===`number`)a[e]=t;else throw Error(`unsupported value for Color.set`);let i=new O(a,r);return n?(this._rgb=i._rgb,this):i}throw Error(`unknown channel ${i} in mode ${r}`)}else return a},O.prototype.tint=function(e=.5,...t){return F(this,`white`,e,...t)},O.prototype.shade=function(e=.5,...t){return F(this,`black`,e,...t)},P.rgb=(e,t,n)=>{let r=e._rgb,i=t._rgb;return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`rgb`)};var{sqrt:je,pow:Me}=Math;P.lrgb=(e,t,n)=>{let[r,i,a]=e._rgb,[o,s,c]=t._rgb;return new O(je(Me(r,2)*(1-n)+Me(o,2)*n),je(Me(i,2)*(1-n)+Me(s,2)*n),je(Me(a,2)*(1-n)+Me(c,2)*n),`rgb`)},P.lab=(e,t,n)=>{let r=e.lab(),i=t.lab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`lab`)};var Ne=(e,t,n,r)=>{let i,a;r===`hsl`?(i=e.hsl(),a=t.hsl()):r===`hsv`?(i=e.hsv(),a=t.hsv()):r===`hcg`?(i=e.hcg(),a=t.hcg()):r===`hsi`?(i=e.hsi(),a=t.hsi()):r===`lch`||r===`hcl`?(r=`hcl`,i=e.hcl(),a=t.hcl()):r===`oklch`&&(i=e.oklch().reverse(),a=t.oklch().reverse());let o,s,c,l,u,d;(r.substr(0,1)===`h`||r===`oklch`)&&([o,c,u]=i,[s,l,d]=a);let f,p,m,h;return!isNaN(o)&&!isNaN(s)?(h=s>o&&s-o>180?s-(o+360):s180?s+360-o:s-o,p=o+n*h):isNaN(o)?isNaN(s)?p=NaN:(p=s,(u==1||u==0)&&r!=`hsv`&&(f=l)):(p=o,(d==1||d==0)&&r!=`hsv`&&(f=c)),f===void 0&&(f=c+n*(l-c)),m=u+n*(d-u),r===`oklch`?new O([m,f,p],r):new O([p,f,m],r)},Pe=(e,t,n)=>Ne(e,t,n,`lch`);P.lch=Pe,P.hcl=Pe;var Fe=e=>{if(h(e)==`number`&&e>=0&&e<=16777215)return[e>>16,e>>8&255,e&255,1];throw Error(`unknown num color: `+e)},Ie=(...e)=>{let[t,n,r]=g(e,`rgb`);return(t<<16)+(n<<8)+r};O.prototype.num=function(){return Ie(this._rgb)},Object.assign(k,{num:(...e)=>new O(...e,`num`)}),D.format.num=Fe,D.autodetect.push({p:5,test:(...e)=>{if(e.length===1&&h(e[0])===`number`&&e[0]>=0&&e[0]<=16777215)return`num`}}),P.num=(e,t,n)=>{let r=e.num();return new O(r+n*(t.num()-r),`num`)};var{floor:Le}=Math,Re=(...e)=>{e=g(e,`hcg`);let[t,n,r]=e,i,a,o;r*=255;let s=n*255;if(n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Le(t),c=t-e,l=r*(1-n),u=l+s*(1-c),d=l+s*c,f=l+s;switch(e){case 0:[i,a,o]=[f,d,l];break;case 1:[i,a,o]=[u,f,l];break;case 2:[i,a,o]=[l,f,d];break;case 3:[i,a,o]=[l,u,f];break;case 4:[i,a,o]=[d,l,f];break;case 5:[i,a,o]=[f,l,u];break}}return[i,a,o,e.length>3?e[3]:1]},ze=(...e)=>{let[t,n,r]=g(e,`rgb`),i=y(t,n,r),a=b(t,n,r),o=a-i,s=o*100/255,c=i/(255-o)*100,l;return o===0?l=NaN:(t===a&&(l=(n-r)/o),n===a&&(l=2+(r-t)/o),r===a&&(l=4+(t-n)/o),l*=60,l<0&&(l+=360)),[l,s,c]};O.prototype.hcg=function(){return ze(this._rgb)},k.hcg=(...e)=>new O(...e,`hcg`),D.format.hcg=Re,D.autodetect.push({p:1,test:(...e)=>{if(e=g(e,`hcg`),h(e)===`array`&&e.length===3)return`hcg`}}),P.hcg=(e,t,n)=>Ne(e,t,n,`hcg`);var{cos:Be}=Math,Ve=(...e)=>{e=g(e,`hsi`);let[t,n,r]=e,i,a,o;return isNaN(t)&&(t=0),isNaN(n)&&(n=0),t>360&&(t-=360),t<0&&(t+=360),t/=360,t<1/3?(o=(1-n)/3,i=(1+n*Be(C*t)/Be(w-C*t))/3,a=1-(o+i)):t<2/3?(t-=1/3,i=(1-n)/3,a=(1+n*Be(C*t)/Be(w-C*t))/3,o=1-(i+a)):(t-=2/3,a=(1-n)/3,o=(1+n*Be(C*t)/Be(w-C*t))/3,i=1-(a+o)),i=f(r*i*3),a=f(r*a*3),o=f(r*o*3),[i*255,a*255,o*255,e.length>3?e[3]:1]},{min:He,sqrt:Ue,acos:We}=Math,Ge=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i,a=He(t,n,r),o=(t+n+r)/3,s=o>0?1-a/o:0;return s===0?i=NaN:(i=(t-n+(t-r))/2,i/=Ue((t-n)*(t-n)+(t-r)*(n-r)),i=We(i),r>n&&(i=C-i),i/=C),[i*360,s,o]};O.prototype.hsi=function(){return Ge(this._rgb)},k.hsi=(...e)=>new O(...e,`hsi`),D.format.hsi=Ve,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsi`),h(e)===`array`&&e.length===3)return`hsi`}}),P.hsi=(e,t,n)=>Ne(e,t,n,`hsi`);var Ke=(...e)=>{e=g(e,`hsl`);let[t,n,r]=e,i,a,o;if(n===0)i=a=o=r*255;else{let e=[0,0,0],s=[0,0,0],c=r<.5?r*(1+n):r+n-r*n,l=2*r-c,u=t/360;e[0]=u+1/3,e[1]=u,e[2]=u-1/3;for(let t=0;t<3;t++)e[t]<0&&(e[t]+=1),e[t]>1&&--e[t],6*e[t]<1?s[t]=l+(c-l)*6*e[t]:2*e[t]<1?s[t]=c:3*e[t]<2?s[t]=l+(c-l)*(2/3-e[t])*6:s[t]=l;[i,a,o]=[s[0]*255,s[1]*255,s[2]*255]}return e.length>3?[i,a,o,e[3]]:[i,a,o,1]},qe=(...e)=>{e=g(e,`rgba`);let[t,n,r]=e;t/=255,n/=255,r/=255;let i=y(t,n,r),a=b(t,n,r),o=(a+i)/2,s,c;return a===i?(s=0,c=NaN):s=o<.5?(a-i)/(a+i):(a-i)/(2-a-i),t==a?c=(n-r)/(a-i):n==a?c=2+(r-t)/(a-i):r==a&&(c=4+(t-n)/(a-i)),c*=60,c<0&&(c+=360),e.length>3&&e[3]!==void 0?[c,s,o,e[3]]:[c,s,o]};O.prototype.hsl=function(){return qe(this._rgb)},k.hsl=(...e)=>new O(...e,`hsl`),D.format.hsl=Ke,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsl`),h(e)===`array`&&e.length===3)return`hsl`}}),P.hsl=(e,t,n)=>Ne(e,t,n,`hsl`);var{floor:Je}=Math,Ye=(...e)=>{e=g(e,`hsv`);let[t,n,r]=e,i,a,o;if(r*=255,n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Je(t),s=t-e,c=r*(1-n),l=r*(1-n*s),u=r*(1-n*(1-s));switch(e){case 0:[i,a,o]=[r,u,c];break;case 1:[i,a,o]=[l,r,c];break;case 2:[i,a,o]=[c,r,u];break;case 3:[i,a,o]=[c,l,r];break;case 4:[i,a,o]=[u,c,r];break;case 5:[i,a,o]=[r,c,l];break}}return[i,a,o,e.length>3?e[3]:1]},{min:Xe,max:Ze}=Math,Qe=(...e)=>{e=g(e,`rgb`);let[t,n,r]=e,i=Xe(t,n,r),a=Ze(t,n,r),o=a-i,s,c,l;return l=a/255,a===0?(s=NaN,c=0):(c=o/a,t===a&&(s=(n-r)/o),n===a&&(s=2+(r-t)/o),r===a&&(s=4+(t-n)/o),s*=60,s<0&&(s+=360)),[s,c,l]};O.prototype.hsv=function(){return Qe(this._rgb)},k.hsv=(...e)=>new O(...e,`hsv`),D.format.hsv=Ye,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsv`),h(e)===`array`&&e.length===3)return`hsv`}}),P.hsv=(e,t,n)=>Ne(e,t,n,`hsv`);function $e(e,t){let n=e.length;Array.isArray(e[0])||(e=[e]),Array.isArray(t[0])||(t=t.map(e=>[e]));let r=t[0].length,i=t[0].map((e,n)=>t.map(e=>e[n])),a=e.map(e=>i.map(t=>Array.isArray(e)?e.reduce((e,n,r)=>e+n*(t[r]||0),0):t.reduce((t,n)=>t+n*e,0)));return n===1&&(a=a[0]),r===1?a.map(e=>e[0]):a}var et=(...e)=>{e=g(e,`lab`);let[t,n,r,...i]=e,[a,o,s]=tt([t,n,r]),[c,l,u]=de(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function tt(e){return $e([[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],$e([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],e).map(e=>e**3))}var nt=(...e)=>{let[t,n,r,...i]=g(e,`rgb`);return[...rt(he(t,n,r)),...i.length>0&&i[0]<1?[i[0]]:[]]};function rt(e){return $e([[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],$e([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],e).map(e=>Math.cbrt(e)))}O.prototype.oklab=function(){return nt(this._rgb)},Object.assign(k,{oklab:(...e)=>new O(...e,`oklab`)}),D.format.oklab=et,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklab`),h(e)===`array`&&e.length===3)return`oklab`}}),P.oklab=(e,t,n)=>{let r=e.oklab(),i=t.oklab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`oklab`)},P.oklch=(e,t,n)=>Ne(e,t,n,`oklch`);var{pow:it,sqrt:at,PI:ot,cos:st,sin:ct,atan2:lt}=Math,ut=(e,t=`lrgb`,n=null)=>{let r=e.length;n||=Array.from(Array(r)).map(()=>1);let i=r/n.reduce(function(e,t){return e+t});if(n.forEach((e,t)=>{n[t]*=i}),e=e.map(e=>new O(e)),t===`lrgb`)return dt(e,n);let a=e.shift(),o=a.get(t),s=[],c=0,l=0;for(let e=0;e{let i=e.get(t);u+=e.alpha()*n[r+1];for(let e=0;e=360;)t-=360;o[e]=t}else o[e]=o[e]/s[e];return u/=r,new O(o,t).alpha(u>.99999?1:u,!0)},dt=(e,t)=>{let n=e.length,r=[0,0,0,0];for(let i=0;i.9999999&&(r[3]=1),new O(p(r))},{pow:ft}=Math;function pt(e){let t=`rgb`,n=k(`#ccc`),r=0,i=[0,1],a=[0,1],o=[],s=[0,0],c=!1,l=[],u=!1,d=0,p=1,m=!1,g={},_=!0,v=1,y=function(e){if(e||=[`#fff`,`#000`],e&&h(e)===`string`&&k.brewer&&k.brewer[e.toLowerCase()]&&(e=k.brewer[e.toLowerCase()]),h(e)===`array`){e.length===1&&(e=[e[0],e[0]]),e=e.slice(0);for(let t=0;t=c[n];)n++;return n-1}return 0},x=e=>e,S=e=>e,C=function(e,r){let i,a;if(r??=!1,isNaN(e)||e===null)return n;a=r?e:c&&c.length>2?b(e)/(c.length-2):p===d?1:(e-d)/(p-d),a=S(a),r||(a=x(a)),v!==1&&(a=ft(a,v)),a=s[0]+a*(1-s[0]-s[1]),a=f(a,0,1);let u=Math.floor(a*1e4);if(_&&g[u])i=g[u];else{if(h(l)===`array`)for(let e=0;e=n&&e===o.length-1){i=l[e];break}if(a>n&&ag={};y(e);let T=function(e){let t=k(C(e));return u&&t[u]?t[u]():t};return T.classes=function(e){if(e!=null){if(h(e)===`array`)c=e,i=[e[0],e[e.length-1]];else{let t=k.analyze(i);c=e===0?[t.min,t.max]:k.limits(t,`e`,e)}return T}return c},T.domain=function(e){if(!arguments.length)return a;a=e.slice(0),d=e[0],p=e[e.length-1],o=[];let t=l.length;if(e.length===t&&d!==p)for(let t of Array.from(e))o.push((t-d)/(p-d));else{for(let e=0;e2){let t=e.map((t,n)=>n/(e.length-1)),n=e.map(e=>(e-d)/(p-d));n.every((e,n)=>t[n]===e)||(S=e=>{if(e<=0||e>=1)return e;let r=0;for(;e>=n[r+1];)r++;let i=(e-n[r])/(n[r+1]-n[r]);return t[r]+i*(t[r+1]-t[r])})}}return i=[d,p],T},T.mode=function(e){return arguments.length?(t=e,w(),T):t},T.range=function(e,t){return y(e,t),T},T.out=function(e){return u=e,T},T.spread=function(e){return arguments.length?(r=e,T):r},T.correctLightness=function(e){return e??=!0,m=e,w(),x=m?function(e){let t=C(0,!0).lab()[0],n=C(1,!0).lab()[0],r=t>n,i=C(e,!0).lab()[0],a=t+(n-t)*e,o=i-a,s=0,c=1,l=20;for(;Math.abs(o)>.01&&l-- >0;)(function(){return r&&(o*=-1),o<0?(s=e,e+=(c-e)*.5):(c=e,e+=(s-e)*.5),i=C(e,!0).lab()[0],o=i-a})();return e}:e=>e,T},T.padding=function(e){return e==null?s:(h(e)===`number`&&(e=[e,e]),s=e,T)},T.colors=function(t,n){arguments.length<2&&(n=`hex`);let r=[];if(arguments.length===0)r=l.slice(0);else if(t===1)r=[T(.5)];else if(t>1){let e=i[0],n=i[1]-e;r=mt(0,t,!1).map(r=>T(e+r/(t-1)*n))}else{e=[];let t=[];if(c&&c.length>2)for(let e=1,n=c.length,r=1<=n;r?en;r?e++:e--)t.push((c[e-1]+c[e])*.5);else t=i;r=t.map(e=>T(e))}return k[n]&&(r=r.map(e=>e[n]())),r},T.cache=function(e){return e==null?_:(_=e,T)},T.gamma=function(e){return e==null?v:(v=e,T)},T.nodata=function(e){return e==null?n:(n=k(e),T)},T}function mt(e,t,n){let r=[],i=ea;i?t++:t--)r.push(t);return r}var ht=function(e){let t=[1,1];for(let n=1;nnew O(e)),e.length===2)[n,r]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>n[t]+e*(r[t]-n[t])),`lab`)};else if(e.length===3)[n,r,i]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*n[t]+2*(1-e)*e*r[t]+e*e*i[t]),`lab`)};else if(e.length===4){let a;[n,r,i,a]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*(1-e)*n[t]+3*(1-e)*(1-e)*e*r[t]+3*(1-e)*e*e*i[t]+e*e*e*a[t]),`lab`)}}else if(e.length>=5){let n,r,i;n=e.map(e=>e.lab()),i=e.length-1,r=ht(i),t=function(e){let t=1-e;return new O([0,1,2].map(a=>n.reduce((n,o,s)=>n+r[s]*t**(i-s)*e**s*o[a],0)),`lab`)}}else throw RangeError(`No point in running bezier with only one color.`);return t},_t=e=>{let t=gt(e);return t.scale=()=>pt(t),t},{round:vt}=Math;O.prototype.rgb=function(e=!0){return e===!1?this._rgb.slice(0,3):this._rgb.slice(0,3).map(vt)},O.prototype.rgba=function(e=!0){return this._rgb.slice(0,4).map((t,n)=>n<3?e===!1?t:vt(t):t)},Object.assign(k,{rgb:(...e)=>new O(...e,`rgb`)}),D.format.rgb=(...e)=>{let t=g(e,`rgba`);return t[3]===void 0&&(t[3]=1),t},D.autodetect.push({p:3,test:(...e)=>{if(e=g(e,`rgba`),h(e)===`array`&&(e.length===3||e.length===4&&h(e[3])==`number`&&e[3]>=0&&e[3]<=1))return`rgb`}});var I=(e,t,n)=>{if(!I[n])throw Error(`unknown blend mode `+n);return I[n](e,t)},L=e=>(t,n)=>{let r=k(n).rgb(),i=k(t).rgb();return k.rgb(e(r,i))},R=e=>(t,n)=>{let r=[];return r[0]=e(t[0],n[0]),r[1]=e(t[1],n[1]),r[2]=e(t[2],n[2]),r};I.normal=L(R(e=>e)),I.multiply=L(R((e,t)=>e*t/255)),I.screen=L(R((e,t)=>255*(1-(1-e/255)*(1-t/255)))),I.overlay=L(R((e,t)=>t<128?2*e*t/255:255*(1-2*(1-e/255)*(1-t/255)))),I.darken=L(R((e,t)=>e>t?t:e)),I.lighten=L(R((e,t)=>e>t?e:t)),I.dodge=L(R((e,t)=>e===255?255:(e=t/255*255/(1-e/255),e>255?255:e))),I.burn=L(R((e,t)=>255*(1-(1-t/255)/(e/255))));var{pow:yt,sin:bt,cos:xt}=Math;function St(e=300,t=-1.5,n=1,r=1,i=[0,1]){let a=0,o;h(i)===`array`?o=i[1]-i[0]:(o=0,i=[i,i]);let s=function(s){let c=C*((e+120)/360+t*s),l=yt(i[0]+o*s,r),u=(a===0?n:n[0]+s*a)*l*(1-l)/2,d=xt(c),f=bt(c),m=l+u*(-.14861*d+1.78277*f),h=l+u*(-.29227*d-.90649*f),g=l+1.97294*d*u;return k(p([m*255,h*255,g*255,1]))};return s.start=function(t){return t==null?e:(e=t,s)},s.rotations=function(e){return e==null?t:(t=e,s)},s.gamma=function(e){return e==null?r:(r=e,s)},s.hue=function(e){return e==null?n:(n=e,h(n)===`array`?(a=n[1]-n[0],a===0&&(n=n[1])):a=0,s)},s.lightness=function(e){return e==null?i:(h(e)===`array`?(i=e,o=e[1]-e[0]):(i=[e,e],o=0),s)},s.scale=()=>k.scale(s),s.hue(n),s}var Ct=`0123456789abcdef`,{floor:wt,random:Tt}=Math,Et=(e=Tt)=>{let t=`#`;for(let n=0;n<6;n++)t+=Ct.charAt(wt(e()*16));return new O(t,`hex`)},{log:Dt,pow:Ot,floor:kt,abs:At}=Math;function jt(e,t=null){let n={min:Number.MAX_VALUE,max:Number.MAX_VALUE*-1,sum:0,values:[],count:0};return h(e)===`object`&&(e=Object.values(e)),e.forEach(e=>{t&&h(e)===`object`&&(e=e[t]),e!=null&&!isNaN(e)&&(n.values.push(e),n.sum+=e,en.max&&(n.max=e),n.count+=1)}),n.domain=[n.min,n.max],n.limits=(e,t)=>Mt(n,e,t),n}function Mt(e,t=`equal`,n=7){h(e)==`array`&&(e=jt(e));let{min:r,max:i}=e,a=e.values.sort((e,t)=>e-t);if(n===1)return[r,i];let o=[];if(t.substr(0,1)===`c`&&(o.push(r),o.push(i)),t.substr(0,1)===`e`){o.push(r);for(let e=1;e 0`);let e=Math.LOG10E*Dt(r),t=Math.LOG10E*Dt(i);o.push(r);for(let r=1;r200&&(l=!1)}let f={};for(let e=0;ee-t),o.push(p[0]);for(let e=1;e{e=new O(e),t=new O(t);let n=e.luminance(),r=t.luminance();return n>r?(n+.05)/(r+.05):(r+.05)/(n+.05)},Pt=.027,Ft=5e-4,It=.1,Lt=1.14,Rt=.022,zt=1.414,Bt=(e,t)=>{e=new O(e),t=new O(t),e.alpha()<1&&(e=F(t,e,e.alpha(),`rgb`));let n=Vt(...e.rgb()),r=Vt(...t.rgb()),i=n>=Rt?n:n+(Rt-n)**+zt,a=r>=Rt?r:r+(Rt-r)**+zt,o=a**.56-i**.57,s=a**.65-i**.62,c=Math.abs(a-i)0?c-Pt:c+Pt)*100};function Vt(e,t,n){return .2126729*(e/255)**2.4+.7151522*(t/255)**2.4+.072175*(n/255)**2.4}var{sqrt:z,pow:B,min:Ht,max:Ut,atan2:Wt,abs:Gt,cos:Kt,sin:qt,exp:Jt,PI:Yt}=Math;function Xt(e,t,n=1,r=1,i=1){var a=function(e){return 360*e/(2*Yt)},o=function(e){return 2*Yt*e/360};e=new O(e),t=new O(t);let[s,c,l]=Array.from(e.lab()),[u,d,f]=Array.from(t.lab()),p=(s+u)/2,m=(z(B(c,2)+B(l,2))+z(B(d,2)+B(f,2)))/2,h=.5*(1-z(B(m,7)/(B(m,7)+B(25,7)))),g=c*(1+h),_=d*(1+h),v=z(B(g,2)+B(l,2)),y=z(B(_,2)+B(f,2)),b=(v+y)/2,x=a(Wt(l,g)),S=a(Wt(f,_)),C=x>=0?x:x+360,w=S>=0?S:S+360,T=Gt(C-w)>180?(C+w+360)/2:(C+w)/2,ee=1-.17*Kt(o(T-30))+.24*Kt(o(2*T))+.32*Kt(o(3*T+6))-.2*Kt(o(4*T-63)),E=w-C;E=Gt(E)<=180?E:w<=C?E+360:E-360,E=2*z(v*y)*qt(o(E)/2);let D=u-s,te=y-v,k=1+.015*B(p-50,2)/z(20+B(p-50,2)),A=1+.045*b,ne=1+.015*b*ee,re=30*Jt(-B((T-275)/25,2)),ie=-(2*z(B(b,7)/(B(b,7)+B(25,7))))*qt(2*o(re));return Ut(0,Ht(100,z(B(D/(n*k),2)+B(te/(r*A),2)+B(E/(i*ne),2)+ie*(te/(r*A))*(E/(i*ne)))))}function Zt(e,t,n=`lab`){e=new O(e),t=new O(t);let r=e.get(n),i=t.get(n),a=0;for(let e in r){let t=(r[e]||0)-(i[e]||0);a+=t*t}return Math.sqrt(a)}var Qt=(...e)=>{try{return new O(...e),!0}catch{return!1}},$t={cool(){return pt([k.hsl(180,1,.9),k.hsl(250,.7,.4)])},hot(){return pt([`#000`,`#f00`,`#ff0`,`#fff`],[0,.25,.75,1]).mode(`rgb`)}},en={OrRd:[`#fff7ec`,`#fee8c8`,`#fdd49e`,`#fdbb84`,`#fc8d59`,`#ef6548`,`#d7301f`,`#b30000`,`#7f0000`],PuBu:[`#fff7fb`,`#ece7f2`,`#d0d1e6`,`#a6bddb`,`#74a9cf`,`#3690c0`,`#0570b0`,`#045a8d`,`#023858`],BuPu:[`#f7fcfd`,`#e0ecf4`,`#bfd3e6`,`#9ebcda`,`#8c96c6`,`#8c6bb1`,`#88419d`,`#810f7c`,`#4d004b`],Oranges:[`#fff5eb`,`#fee6ce`,`#fdd0a2`,`#fdae6b`,`#fd8d3c`,`#f16913`,`#d94801`,`#a63603`,`#7f2704`],BuGn:[`#f7fcfd`,`#e5f5f9`,`#ccece6`,`#99d8c9`,`#66c2a4`,`#41ae76`,`#238b45`,`#006d2c`,`#00441b`],YlOrBr:[`#ffffe5`,`#fff7bc`,`#fee391`,`#fec44f`,`#fe9929`,`#ec7014`,`#cc4c02`,`#993404`,`#662506`],YlGn:[`#ffffe5`,`#f7fcb9`,`#d9f0a3`,`#addd8e`,`#78c679`,`#41ab5d`,`#238443`,`#006837`,`#004529`],Reds:[`#fff5f0`,`#fee0d2`,`#fcbba1`,`#fc9272`,`#fb6a4a`,`#ef3b2c`,`#cb181d`,`#a50f15`,`#67000d`],RdPu:[`#fff7f3`,`#fde0dd`,`#fcc5c0`,`#fa9fb5`,`#f768a1`,`#dd3497`,`#ae017e`,`#7a0177`,`#49006a`],Greens:[`#f7fcf5`,`#e5f5e0`,`#c7e9c0`,`#a1d99b`,`#74c476`,`#41ab5d`,`#238b45`,`#006d2c`,`#00441b`],YlGnBu:[`#ffffd9`,`#edf8b1`,`#c7e9b4`,`#7fcdbb`,`#41b6c4`,`#1d91c0`,`#225ea8`,`#253494`,`#081d58`],Purples:[`#fcfbfd`,`#efedf5`,`#dadaeb`,`#bcbddc`,`#9e9ac8`,`#807dba`,`#6a51a3`,`#54278f`,`#3f007d`],GnBu:[`#f7fcf0`,`#e0f3db`,`#ccebc5`,`#a8ddb5`,`#7bccc4`,`#4eb3d3`,`#2b8cbe`,`#0868ac`,`#084081`],Greys:[`#ffffff`,`#f0f0f0`,`#d9d9d9`,`#bdbdbd`,`#969696`,`#737373`,`#525252`,`#252525`,`#000000`],YlOrRd:[`#ffffcc`,`#ffeda0`,`#fed976`,`#feb24c`,`#fd8d3c`,`#fc4e2a`,`#e31a1c`,`#bd0026`,`#800026`],PuRd:[`#f7f4f9`,`#e7e1ef`,`#d4b9da`,`#c994c7`,`#df65b0`,`#e7298a`,`#ce1256`,`#980043`,`#67001f`],Blues:[`#f7fbff`,`#deebf7`,`#c6dbef`,`#9ecae1`,`#6baed6`,`#4292c6`,`#2171b5`,`#08519c`,`#08306b`],PuBuGn:[`#fff7fb`,`#ece2f0`,`#d0d1e6`,`#a6bddb`,`#67a9cf`,`#3690c0`,`#02818a`,`#016c59`,`#014636`],Viridis:[`#440154`,`#482777`,`#3f4a8a`,`#31678e`,`#26838f`,`#1f9d8a`,`#6cce5a`,`#b6de2b`,`#fee825`],Spectral:[`#9e0142`,`#d53e4f`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#e6f598`,`#abdda4`,`#66c2a5`,`#3288bd`,`#5e4fa2`],RdYlGn:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#d9ef8b`,`#a6d96a`,`#66bd63`,`#1a9850`,`#006837`],RdBu:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#f7f7f7`,`#d1e5f0`,`#92c5de`,`#4393c3`,`#2166ac`,`#053061`],PiYG:[`#8e0152`,`#c51b7d`,`#de77ae`,`#f1b6da`,`#fde0ef`,`#f7f7f7`,`#e6f5d0`,`#b8e186`,`#7fbc41`,`#4d9221`,`#276419`],PRGn:[`#40004b`,`#762a83`,`#9970ab`,`#c2a5cf`,`#e7d4e8`,`#f7f7f7`,`#d9f0d3`,`#a6dba0`,`#5aae61`,`#1b7837`,`#00441b`],RdYlBu:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee090`,`#ffffbf`,`#e0f3f8`,`#abd9e9`,`#74add1`,`#4575b4`,`#313695`],BrBG:[`#543005`,`#8c510a`,`#bf812d`,`#dfc27d`,`#f6e8c3`,`#f5f5f5`,`#c7eae5`,`#80cdc1`,`#35978f`,`#01665e`,`#003c30`],RdGy:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#ffffff`,`#e0e0e0`,`#bababa`,`#878787`,`#4d4d4d`,`#1a1a1a`],PuOr:[`#7f3b08`,`#b35806`,`#e08214`,`#fdb863`,`#fee0b6`,`#f7f7f7`,`#d8daeb`,`#b2abd2`,`#8073ac`,`#542788`,`#2d004b`],Set2:[`#66c2a5`,`#fc8d62`,`#8da0cb`,`#e78ac3`,`#a6d854`,`#ffd92f`,`#e5c494`,`#b3b3b3`],Accent:[`#7fc97f`,`#beaed4`,`#fdc086`,`#ffff99`,`#386cb0`,`#f0027f`,`#bf5b17`,`#666666`],Set1:[`#e41a1c`,`#377eb8`,`#4daf4a`,`#984ea3`,`#ff7f00`,`#ffff33`,`#a65628`,`#f781bf`,`#999999`],Set3:[`#8dd3c7`,`#ffffb3`,`#bebada`,`#fb8072`,`#80b1d3`,`#fdb462`,`#b3de69`,`#fccde5`,`#d9d9d9`,`#bc80bd`,`#ccebc5`,`#ffed6f`],Dark2:[`#1b9e77`,`#d95f02`,`#7570b3`,`#e7298a`,`#66a61e`,`#e6ab02`,`#a6761d`,`#666666`],Paired:[`#a6cee3`,`#1f78b4`,`#b2df8a`,`#33a02c`,`#fb9a99`,`#e31a1c`,`#fdbf6f`,`#ff7f00`,`#cab2d6`,`#6a3d9a`,`#ffff99`,`#b15928`],Pastel2:[`#b3e2cd`,`#fdcdac`,`#cbd5e8`,`#f4cae4`,`#e6f5c9`,`#fff2ae`,`#f1e2cc`,`#cccccc`],Pastel1:[`#fbb4ae`,`#b3cde3`,`#ccebc5`,`#decbe4`,`#fed9a6`,`#ffffcc`,`#e5d8bd`,`#fddaec`,`#f2f2f2`]},tn=Object.keys(en),nn=new Map(tn.map(e=>[e.toLowerCase(),e])),rn=typeof Proxy==`function`?new Proxy(en,{get(e,t){let n=t.toLowerCase();if(nn.has(n))return e[nn.get(n)]},getOwnPropertyNames(){return Object.getOwnPropertyNames(tn)}}):en,an=(...e)=>{e=g(e,`cmyk`);let[t,n,r,i]=e,a=e.length>4?e[4]:1;return i===1?[0,0,0,a]:[t>=1?0:255*(1-t)*(1-i),n>=1?0:255*(1-n)*(1-i),r>=1?0:255*(1-r)*(1-i),a]},{max:on}=Math,sn=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i=1-on(t,on(n,r)),a=i<1?1/(1-i):0;return[(1-t-i)*a,(1-n-i)*a,(1-r-i)*a,i]};O.prototype.cmyk=function(){return sn(this._rgb)},Object.assign(k,{cmyk:(...e)=>new O(...e,`cmyk`)}),D.format.cmyk=an,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`cmyk`),h(e)===`array`&&e.length===4)return`cmyk`}});var cn=(...e)=>{let t=g(e,`hsla`),n=_(e)||`lsa`;return t[0]=x(t[0]||0)+`deg`,t[1]=x(t[1]*100)+`%`,t[2]=x(t[2]*100)+`%`,n===`hsla`||t.length>3&&t[3]<1?(t[3]=`/ `+(t.length>3?t[3]:1),n=`hsla`):t.length=3,`${n.substr(0,3)}(${t.join(` `)})`},ln=(...e)=>{let t=g(e,`lab`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=x(t[2]),n===`laba`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lab(${t.join(` `)})`},un=(...e)=>{let t=g(e,`lch`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,n===`lcha`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lch(${t.join(` `)})`},dn=(...e)=>{let t=g(e,`lab`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=S(t[2]),t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklab(${t.join(` `)})`},fn=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=nt(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},pn=(...e)=>{let t=g(e,`lch`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklch(${t.join(` `)})`},{round:mn}=Math,hn=(...e)=>{let t=g(e,`rgba`),n=_(e)||`rgb`;if(n.substr(0,3)===`hsl`)return cn(qe(t),n);if(n.substr(0,3)===`lab`){let e=ce();M(`d50`);let r=ln(fe(t),n);return M(e),r}if(n.substr(0,3)===`lch`){let e=ce();M(`d50`);let r=un(Ae(t),n);return M(e),r}return n.substr(0,5)===`oklab`?dn(nt(t)):n.substr(0,5)===`oklch`?pn(fn(t)):(t[0]=mn(t[0]),t[1]=mn(t[1]),t[2]=mn(t[2]),(n===`rgba`||t.length>3&&t[3]<1)&&(t[3]=`/ `+(t.length>3?t[3]:1),n=`rgba`),`${n.substr(0,3)}(${t.slice(0,n===`rgb`?3:4).join(` `)})`)},gn=(...e)=>{e=g(e,`lch`);let[t,n,r,...i]=e,[a,o,s]=Ce(t,n,r),[c,l,u]=et(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},V=`((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)`,H=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)`,_n=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)`,U=`\\s*`,vn=`\\s+`,yn=`\\s*,\\s*`,bn=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)`,xn=`\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?`,Sn=RegExp(`^rgba?\\(`+U+[V,V,V].join(vn)+xn+`\\)$`),Cn=RegExp(`^rgb\\(`+U+[V,V,V].join(yn)+U+`\\)$`),wn=RegExp(`^rgba\\(`+U+[V,V,V,H].join(yn)+U+`\\)$`),Tn=RegExp(`^hsla?\\(`+U+[bn,_n,_n].join(vn)+xn+`\\)$`),En=RegExp(`^hsl?\\(`+U+[bn,_n,_n].join(yn)+U+`\\)$`),Dn=/^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,On=RegExp(`^lab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),kn=RegExp(`^lch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),An=RegExp(`^oklab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),jn=RegExp(`^oklch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),{round:Mn}=Math,Nn=e=>e.map((e,t)=>t<=2?f(Mn(e),0,255):e),W=(e,t=0,n=100,r=!1)=>(typeof e==`string`&&e.endsWith(`%`)&&(e=parseFloat(e.substring(0,e.length-1))/100,e=r?t+(e+1)*.5*(n-t):t+e*(n-t)),+e),G=(e,t)=>e===`none`?t:e,Pn=e=>{if(e=e.toLowerCase().trim(),e===`transparent`)return[0,0,0,0];let t;if(D.format.named)try{return D.format.named(e)}catch{}if((t=e.match(Sn))||(t=e.match(Cn))){let e=t.slice(1,4);for(let t=0;t<3;t++)e[t]=+W(G(e[t],0),0,255);e=Nn(e);let n=t[4]===void 0?1:+W(t[4],0,1);return e[3]=n,e}if(t=e.match(wn)){let e=t.slice(1,5);for(let t=0;t<4;t++)e[t]=+W(e[t],0,255);return e}if((t=e.match(Tn))||(t=e.match(En))){let e=t.slice(1,4);e[0]=+G(e[0].replace(`deg`,``),0),e[1]=W(G(e[1],0),0,100)*.01,e[2]=W(G(e[2],0),0,100)*.01;let n=Nn(Ke(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(Dn)){let e=t.slice(1,4);e[1]*=.01,e[2]*=.01;let n=Ke(e);for(let e=0;e<3;e++)n[e]=Mn(n[e]);return n[3]=+t[4],n}if(t=e.match(On)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,100),e[1]=W(G(e[1],0),-125,125,!0),e[2]=W(G(e[2],0),-125,125,!0);let n=ce();M(`d50`);let r=Nn(N(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(kn)){let e=t.slice(1,4);e[0]=W(e[0],0,100),e[1]=W(G(e[1],0),0,150,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=ce();M(`d50`);let r=Nn(we(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(An)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),-.4,.4,!0),e[2]=W(G(e[2],0),-.4,.4,!0);let n=Nn(et(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(jn)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),0,.4,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=Nn(gn(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}};Pn.test=e=>Sn.test(e)||Tn.test(e)||On.test(e)||kn.test(e)||An.test(e)||jn.test(e)||Cn.test(e)||wn.test(e)||En.test(e)||Dn.test(e)||e===`transparent`,O.prototype.css=function(e){return hn(this._rgb,e)},k.css=(...e)=>new O(...e,`css`),D.format.css=Pn,D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&Pn.test(e))return`css`}}),D.format.gl=(...e)=>{let t=g(e,`rgba`);return t[0]*=255,t[1]*=255,t[2]*=255,t},k.gl=(...e)=>new O(...e,`gl`),O.prototype.gl=function(){let e=this._rgb;return[e[0]/255,e[1]/255,e[2]/255,e[3]]},O.prototype.hex=function(e){return oe(this._rgb,e)},k.hex=(...e)=>new O(...e,`hex`),D.format.hex=ie,D.autodetect.push({p:4,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&[3,4,5,6,7,8,9].indexOf(e.length)>=0)return`hex`}});var{log:Fn}=Math,In=e=>{let t=e/100,n,r,i;return t<66?(n=255,r=t<6?0:-155.25485562709179-.44596950469579133*(r=t-2)+104.49216199393888*Fn(r),i=t<20?0:-254.76935184120902+.8274096064007395*(i=t-10)+115.67994401066147*Fn(i)):(n=351.97690566805693+.114206453784165*(n=t-55)-40.25366309332127*Fn(n),r=325.4494125711974+.07943456536662342*(r=t-50)-28.0852963507957*Fn(r),i=255),[n,r,i,1]},{round:Ln}=Math,Rn=(...e)=>{let t=g(e,`rgb`),n=t[0],r=t[2],i=1e3,a=4e4,o;for(;a-i>.4;){o=(a+i)*.5;let e=In(o);e[2]/e[0]>=r/n?a=o:i=o}return Ln(o)};O.prototype.temp=O.prototype.kelvin=O.prototype.temperature=function(){return Rn(this._rgb)};var zn=(...e)=>new O(...e,`temp`);Object.assign(k,{temp:zn,kelvin:zn,temperature:zn}),D.format.temp=D.format.kelvin=D.format.temperature=In,O.prototype.oklch=function(){return fn(this._rgb)},Object.assign(k,{oklch:(...e)=>new O(...e,`oklch`)}),D.format.oklch=gn,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklch`),h(e)===`array`&&e.length===3)return`oklch`}}),Object.assign(k,{analyze:jt,average:ut,bezier:_t,blend:I,brewer:rn,Color:O,colors:A,contrast:Nt,contrastAPCA:Bt,cubehelix:St,deltaE:Xt,distance:Zt,input:D,interpolate:F,limits:Mt,mix:F,random:Et,scale:pt,scales:$t,valid:Qt});var K=k,q=class e{constructor(){this.hex=`#000000`,this.rgb_r=0,this.rgb_g=0,this.rgb_b=0,this.xyz_x=0,this.xyz_y=0,this.xyz_z=0,this.luv_l=0,this.luv_u=0,this.luv_v=0,this.lch_l=0,this.lch_c=0,this.lch_h=0,this.hsluv_h=0,this.hsluv_s=0,this.hsluv_l=0,this.hpluv_h=0,this.hpluv_p=0,this.hpluv_l=0,this.r0s=0,this.r0i=0,this.r1s=0,this.r1i=0,this.g0s=0,this.g0i=0,this.g1s=0,this.g1i=0,this.b0s=0,this.b0i=0,this.b1s=0,this.b1i=0}static fromLinear(e){return e<=.0031308?12.92*e:1.055*e**(1/2.4)-.055}static toLinear(e){return e>.04045?((e+.055)/1.055)**2.4:e/12.92}static yToL(t){return t<=e.epsilon?t/e.refY*e.kappa:116*(t/e.refY)**(1/3)-16}static lToY(t){return t<=8?e.refY*t/e.kappa:e.refY*((t+16)/116)**3}static rgbChannelToHex(t){let n=Math.round(t*255),r=n%16,i=(n-r)/16|0;return e.hexChars.charAt(i)+e.hexChars.charAt(r)}static hexToRgbChannel(t,n){let r=e.hexChars.indexOf(t.charAt(n)),i=e.hexChars.indexOf(t.charAt(n+1));return(r*16+i)/255}static distanceFromOriginAngle(e,t,n){let r=t/(Math.sin(n)-e*Math.cos(n));return r<0?1/0:r}static distanceFromOrigin(e,t){return Math.abs(t)/Math.sqrt(e**2+1)}static min6(e,t,n,r,i,a){return Math.min(e,Math.min(t,Math.min(n,Math.min(r,Math.min(i,a)))))}rgbToHex(){this.hex=`#`,this.hex+=e.rgbChannelToHex(this.rgb_r),this.hex+=e.rgbChannelToHex(this.rgb_g),this.hex+=e.rgbChannelToHex(this.rgb_b)}hexToRgb(){this.hex=this.hex.toLowerCase(),this.rgb_r=e.hexToRgbChannel(this.hex,1),this.rgb_g=e.hexToRgbChannel(this.hex,3),this.rgb_b=e.hexToRgbChannel(this.hex,5)}xyzToRgb(){this.rgb_r=e.fromLinear(e.m_r0*this.xyz_x+e.m_r1*this.xyz_y+e.m_r2*this.xyz_z),this.rgb_g=e.fromLinear(e.m_g0*this.xyz_x+e.m_g1*this.xyz_y+e.m_g2*this.xyz_z),this.rgb_b=e.fromLinear(e.m_b0*this.xyz_x+e.m_b1*this.xyz_y+e.m_b2*this.xyz_z)}rgbToXyz(){let t=e.toLinear(this.rgb_r),n=e.toLinear(this.rgb_g),r=e.toLinear(this.rgb_b);this.xyz_x=.41239079926595*t+.35758433938387*n+.18048078840183*r,this.xyz_y=.21263900587151*t+.71516867876775*n+.072192315360733*r,this.xyz_z=.019330818715591*t+.11919477979462*n+.95053215224966*r}xyzToLuv(){let t=this.xyz_x+15*this.xyz_y+3*this.xyz_z,n=4*this.xyz_x,r=9*this.xyz_y;t===0?(n=NaN,r=NaN):(n/=t,r/=t),this.luv_l=e.yToL(this.xyz_y),this.luv_l===0?(this.luv_u=0,this.luv_v=0):(this.luv_u=13*this.luv_l*(n-e.refU),this.luv_v=13*this.luv_l*(r-e.refV))}luvToXyz(){if(this.luv_l===0){this.xyz_x=0,this.xyz_y=0,this.xyz_z=0;return}let t=this.luv_u/(13*this.luv_l)+e.refU,n=this.luv_v/(13*this.luv_l)+e.refV;this.xyz_y=e.lToY(this.luv_l),this.xyz_x=0-9*this.xyz_y*t/((t-4)*n-t*n),this.xyz_z=(9*this.xyz_y-15*n*this.xyz_y-n*this.xyz_x)/(3*n)}luvToLch(){this.lch_l=this.luv_l,this.lch_c=Math.sqrt(this.luv_u*this.luv_u+this.luv_v*this.luv_v),this.lch_c<1e-8?this.lch_h=0:(this.lch_h=Math.atan2(this.luv_v,this.luv_u)*180/Math.PI,this.lch_h<0&&(this.lch_h=360+this.lch_h))}lchToLuv(){let e=this.lch_h/180*Math.PI;this.luv_l=this.lch_l,this.luv_u=Math.cos(e)*this.lch_c,this.luv_v=Math.sin(e)*this.lch_c}calculateBoundingLines(t){let n=(t+16)**3/1560896,r=n>e.epsilon?n:t/e.kappa,i=r*(284517*e.m_r0-94839*e.m_r2),a=r*(838422*e.m_r2+769860*e.m_r1+731718*e.m_r0),o=r*(632260*e.m_r2-126452*e.m_r1),s=r*(284517*e.m_g0-94839*e.m_g2),c=r*(838422*e.m_g2+769860*e.m_g1+731718*e.m_g0),l=r*(632260*e.m_g2-126452*e.m_g1),u=r*(284517*e.m_b0-94839*e.m_b2),d=r*(838422*e.m_b2+769860*e.m_b1+731718*e.m_b0),f=r*(632260*e.m_b2-126452*e.m_b1);this.r0s=i/o,this.r0i=a*t/o,this.r1s=i/(o+126452),this.r1i=(a-769860)*t/(o+126452),this.g0s=s/l,this.g0i=c*t/l,this.g1s=s/(l+126452),this.g1i=(c-769860)*t/(l+126452),this.b0s=u/f,this.b0i=d*t/f,this.b1s=u/(f+126452),this.b1i=(d-769860)*t/(f+126452)}calcMaxChromaHpluv(){let t=e.distanceFromOrigin(this.r0s,this.r0i),n=e.distanceFromOrigin(this.r1s,this.r1i),r=e.distanceFromOrigin(this.g0s,this.g0i),i=e.distanceFromOrigin(this.g1s,this.g1i),a=e.distanceFromOrigin(this.b0s,this.b0i),o=e.distanceFromOrigin(this.b1s,this.b1i);return e.min6(t,n,r,i,a,o)}calcMaxChromaHsluv(t){let n=t/360*Math.PI*2,r=e.distanceFromOriginAngle(this.r0s,this.r0i,n),i=e.distanceFromOriginAngle(this.r1s,this.r1i,n),a=e.distanceFromOriginAngle(this.g0s,this.g0i,n),o=e.distanceFromOriginAngle(this.g1s,this.g1i,n),s=e.distanceFromOriginAngle(this.b0s,this.b0i,n),c=e.distanceFromOriginAngle(this.b1s,this.b1i,n);return e.min6(r,i,a,o,s,c)}hsluvToLch(){this.hsluv_l>99.9999999?(this.lch_l=100,this.lch_c=0):this.hsluv_l<1e-8?(this.lch_l=0,this.lch_c=0):(this.lch_l=this.hsluv_l,this.calculateBoundingLines(this.hsluv_l),this.lch_c=this.calcMaxChromaHsluv(this.hsluv_h)/100*this.hsluv_s),this.lch_h=this.hsluv_h}lchToHsluv(){if(this.lch_l>99.9999999)this.hsluv_s=0,this.hsluv_l=100;else if(this.lch_l<1e-8)this.hsluv_s=0,this.hsluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHsluv(this.lch_h);this.hsluv_s=this.lch_c/e*100,this.hsluv_l=this.lch_l}this.hsluv_h=this.lch_h}hpluvToLch(){this.hpluv_l>99.9999999?(this.lch_l=100,this.lch_c=0):this.hpluv_l<1e-8?(this.lch_l=0,this.lch_c=0):(this.lch_l=this.hpluv_l,this.calculateBoundingLines(this.hpluv_l),this.lch_c=this.calcMaxChromaHpluv()/100*this.hpluv_p),this.lch_h=this.hpluv_h}lchToHpluv(){if(this.lch_l>99.9999999)this.hpluv_p=0,this.hpluv_l=100;else if(this.lch_l<1e-8)this.hpluv_p=0,this.hpluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHpluv();this.hpluv_p=this.lch_c/e*100,this.hpluv_l=this.lch_l}this.hpluv_h=this.lch_h}hsluvToRgb(){this.hsluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hpluvToRgb(){this.hpluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hsluvToHex(){this.hsluvToRgb(),this.rgbToHex()}hpluvToHex(){this.hpluvToRgb(),this.rgbToHex()}rgbToHsluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHsluv()}rgbToHpluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHpluv()}hexToHsluv(){this.hexToRgb(),this.rgbToHsluv()}hexToHpluv(){this.hexToRgb(),this.rgbToHpluv()}};q.hexChars=`0123456789abcdef`,q.refY=1,q.refU=.19783000664283,q.refV=.46831999493879,q.kappa=903.2962962,q.epsilon=.0088564516,q.m_r0=3.240969941904521,q.m_r1=-1.537383177570093,q.m_r2=-.498610760293,q.m_g0=-.96924363628087,q.m_g1=1.87596750150772,q.m_g2=.041555057407175,q.m_b0=.055630079696993,q.m_b1=-.20397695888897,q.m_b2=1.056971514242878;var Bn=s(((e,t)=>{function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.exports=n})),Vn=s(((e,t)=>{var n=Bn(),r,i;function a(){for(var e in i=[`toString`,`toLocaleString`,`valueOf`,`hasOwnProperty`,`isPrototypeOf`,`propertyIsEnumerable`,`constructor`],r=!0,{toString:null})r=!1}function o(e,t,o){var c,l=0;for(c in r??a(),e)if(s(t,e,c,o)===!1)break;if(r)for(var u=e.constructor,d=!!u&&e===u.prototype;(c=i[l++])&&!((c!==`constructor`||!d&&n(e,c))&&e[c]!==Object.prototype[c]&&s(t,e,c,o)===!1););}function s(e,t,n,r){return e.call(r,t[n],n,t)}t.exports=o})),Hn=s(((e,t)=>{var n=Vn();function r(e){var t=[];return n(e,function(e,n){typeof e==`function`&&t.push(n)}),t.sort()}t.exports=r})),Un=s(((e,t)=>{function n(e,t,n){var r=e.length;t=t==null?0:t<0?Math.max(r+t,0):Math.min(t,r),n=n==null?r:n<0?Math.max(r+n,0):Math.min(n,r);for(var i=[];t{var n=Un();function r(e,t,r){var i=n(arguments,2);return function(){return e.apply(t,i.concat(n(arguments)))}}t.exports=r})),Gn=s(((e,t)=>{function n(e,t,n){if(e!=null)for(var r=-1,i=e.length;++r{var n=Hn(),r=Wn(),i=Gn(),a=Un();function o(e,t){i(arguments.length>1?a(arguments,1):n(e),function(t){e[t]=r(e[t],e)})}t.exports=o})),J=s(((e,t)=>{var n=Bn(),r=Vn();function i(e,t,i){r(e,function(r,a){if(n(e,a))return t.call(i,e[a],a,e)})}t.exports=i})),qn=s(((e,t)=>{function n(e){return e}t.exports=n})),Jn=s(((e,t)=>{function n(e){return function(t){return t[e]}}t.exports=n})),Yn=s(((e,t)=>{var n=/^\[object (.*)\]$/,r=Object.prototype.toString,i;function a(e){return e===null?`Null`:e===i?`Undefined`:n.exec(r.call(e))[1]}t.exports=a})),Xn=s(((e,t)=>{var n=Yn();function r(e,t){return n(e)===t}t.exports=r})),Zn=s(((e,t)=>{var n=Xn();t.exports=Array.isArray||function(e){return n(e,`Array`)}})),Qn=s(((e,t)=>{var n=J(),r=Zn();function i(e,t){for(var n=-1,r=e.length;++n{var n=qn(),r=Jn(),i=Qn();function a(e,t){if(e==null)return n;switch(typeof e){case`function`:return t===void 0?e:function(n,r,i){return e.call(t,n,r,i)};case`object`:return function(t){return i(t,e)};case`string`:case`number`:return r(e)}}t.exports=a})),$n=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!1;return n(e,function(n,r){if(t(n,r,e))return a=!0,!1}),a}t.exports=i})),er=s(((e,t)=>{var n=$n();function r(e,t){return n(e,function(e){return e===t})}t.exports=r})),tr=s(((e,t)=>{function n(e){return!!e&&typeof e==`object`&&e.constructor===Object}t.exports=n})),nr=s(((e,t)=>{var n=J(),r=tr();function i(e,t){for(var a=0,o=arguments.length,s;++a{var n=J(),r=tr();function i(e,t){for(var r=0,i=arguments.length,o;++r{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!0;return n(e,function(n,r){if(!t(n,r,e))return a=!1,!1}),a}t.exports=i})),ar=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Object`)}t.exports=r})),or=s(((e,t)=>{function n(e,t){return e===t?e!==0||1/e==1/t:e!==e&&t!==t}t.exports=n})),sr=s(((e,t)=>{var n=Bn(),r=ir(),i=ar(),a=or();function o(e){return function(t,r){return n(this,r)&&e(t,this[r])}}function s(e,t){return n(this,t)}function c(e,t,n){return n||=a,!i(e)||!i(t)?n(e,t):r(e,o(n),t)&&r(t,s,e)}t.exports=c})),cr=s(((e,t)=>{var n=Gn(),r=Un(),i=J();function a(e,t){return n(r(arguments,1),function(t){i(t,function(t,n){e[n]??(e[n]=t)})}),e}t.exports=a})),lr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){t(e,n,r)&&(a[n]=e)}),a}t.exports=i})),ur=s(((e,t)=>{var n=$n(),r=Y();function i(e,t,i){t=r(t,i);var a;return n(e,function(e,n,r){if(t(e,n,r))return a=e,!0}),a}t.exports=i})),dr=s(((e,t)=>{var n=J(),r=tr();function i(e,t,a,o){return n(e,function(e,n){var s=a?a+`.`+n:n;o!==0&&r(e)?i(e,t,s,o-1):t[s]=e}),t}function a(e,t){return e==null?{}:(t??=-1,i(e,{},``,t))}t.exports=a})),fr=s(((e,t)=>{function n(e){switch(typeof e){case`string`:case`number`:case`boolean`:return!0}return e==null}t.exports=n})),pr=s(((e,t)=>{fr();function n(e,t){for(var n=t.split(`.`),r=n.pop();t=n.shift();)if(e=e[t],e==null)return;return e[r]}t.exports=n})),mr=s(((e,t)=>{var n=pr(),r;function i(e,t){return n(e,t)!==r}t.exports=i})),hr=s(((e,t)=>{var n=J();t.exports=Object.keys||function(e){var t=[];return n(e,function(e,n){t.push(n)}),t}})),gr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){a[n]=t(e,n,r)}),a}t.exports=i})),_r=s(((e,t)=>{var n=J();function r(e,t){var r=!0;return n(t,function(t,n){if(e[n]!==t)return r=!1}),r}t.exports=r})),vr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return 1/0;if(e.length&&!t)return Math.max.apply(Math,e);t=n(t,r);for(var i,a=-1/0,o,s,c=-1,l=e.length;++ca&&(a=s,i=o);return i}t.exports=r})),yr=s(((e,t)=>{var n=J();function r(e){var t=[];return n(e,function(e,n){t.push(e)}),t}t.exports=r})),br=s(((e,t)=>{var n=vr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),xr=s(((e,t)=>{var n=J();function r(e,t){for(var r=0,a=arguments.length,o;++r{var n=Yn(),r=tr(),i=xr();function a(e){switch(n(e)){case`Object`:return o(e);case`Array`:return l(e);case`RegExp`:return s(e);case`Date`:return c(e);default:return e}}function o(e){return r(e)?i({},e):e}function s(e){var t=``;return t+=e.multiline?`m`:``,t+=e.global?`g`:``,t+=e.ignoreCase?`i`:``,new RegExp(e.source,t)}function c(e){return new Date(+e)}function l(e){return e.slice()}t.exports=a})),Cr=s(((e,t)=>{var n=Sr(),r=J(),i=Yn(),a=tr();function o(e,t){switch(i(e)){case`Object`:return s(e,t);case`Array`:return c(e,t);default:return n(e)}}function s(e,t){if(a(e)){var n={};return r(e,function(e,n){this[n]=o(e,t)},n),n}else if(t)return t(e);else return e}function c(e,t){for(var n=[],r=-1,i=e.length;++r{var n=Bn(),r=Cr(),i=ar();function a(){for(var e=1,t,o,s,c=r(arguments[0]);s=arguments[e++];)for(t in s)n(s,t)&&(o=s[t],i(o)&&i(c[t])?c[t]=a(c[t],o):c[t]=r(o));return c}t.exports=a})),Tr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return-1/0;if(e.length&&!t)return Math.min.apply(Math,e);t=n(t,r);for(var i,a=1/0,o,s,c=-1,l=e.length;++c{var n=Tr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),Dr=s(((e,t)=>{var n=Gn();function r(e,t){return t&&n(t.split(`.`),function(t){e[t]||(e[t]={}),e=e[t]}),e}t.exports=r})),Or=s(((e,t)=>{function n(e,t,n){if(n||=0,e==null)return-1;for(var r=e.length,i=n<0?r+n:n;i{var n=Or();function r(e,t){return n(e,t)!==-1}t.exports=r})),Ar=s(((e,t)=>{var n=Un(),r=kr();function i(e,t){var i=typeof arguments[1]==`string`?n(arguments,1):arguments[1],a={};for(var o in e)e.hasOwnProperty(o)&&!r(i,o)&&(a[o]=e[o]);return a}t.exports=i})),jr=s(((e,t)=>{var n=Un();function r(e,t){for(var r=typeof arguments[1]==`string`?n(arguments,1):arguments[1],i={},a=0,o;o=r[a++];)i[o]=e[o];return i}t.exports=r})),Mr=s(((e,t)=>{var n=gr(),r=Jn();function i(e,t){return n(e,r(t))}t.exports=i})),Nr=s(((e,t)=>{var n=J();function r(e){var t=0;return n(e,function(){t++}),t}t.exports=r})),Pr=s(((e,t)=>{var n=J(),r=Nr();function i(e,t,i,a){var o=arguments.length>2;if(!r(e)&&!o)throw Error(`reduce of empty object with no initial value`);return n(e,function(e,n,r){o?i=t.call(a,i,e,n,r):(i=e,o=!0)}),i}t.exports=i})),Fr=s(((e,t)=>{var n=lr(),r=Y();function i(e,t,i){return t=r(t,i),n(e,function(e,n,r){return!t(e,n,r)},i)}t.exports=i})),Ir=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Function`)}t.exports=r})),Lr=s(((e,t)=>{var n=Ir();function r(e,t){var r=e[t];if(r!==void 0)return n(r)?r.call(e):r}t.exports=r})),Rr=s(((e,t)=>{var n=Dr();function r(e,t,r){var i=/^(.+)\.(.+)$/.exec(t);i?n(e,i[1])[i[2]]=r:e[t]=r}t.exports=r})),zr=s(((e,t)=>{var n=mr();function r(e,t){if(n(e,t)){for(var r=t.split(`.`),i=r.pop();t=r.shift();)e=e[t];return delete e[i]}else return!0}t.exports=r})),Br=s(((e,t)=>{t.exports={bindAll:Kn(),contains:er(),deepFillIn:nr(),deepMatches:Qn(),deepMixIn:rr(),equals:sr(),every:ir(),fillIn:cr(),filter:lr(),find:ur(),flatten:dr(),forIn:Vn(),forOwn:J(),functions:Hn(),get:pr(),has:mr(),hasOwn:Bn(),keys:hr(),map:gr(),matches:_r(),max:br(),merge:wr(),min:Er(),mixIn:xr(),namespace:Dr(),omit:Ar(),pick:jr(),pluck:Mr(),reduce:Pr(),reject:Fr(),result:Lr(),set:Rr(),size:Nr(),some:$n(),unset:zr(),values:yr()}})),Vr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.default=(0,Br().map)({A:{x:.44758,y:.40745},C:{x:.31006,y:.31616},D50:{x:.34567,y:.35851},D65:{x:.31272,y:.32903},D55:{x:.33243,y:.34744},D75:{x:.29903,y:.31488}},function(e){return[100*(e.x/e.y),100,100*(1-e.x-e.y)/e.y]}),t.exports=e.default})),Hr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=Math,r=n.pow,i=n.sign,a=n.abs,o={decode:function(e){return e<=.04045?e/12.92:r((e+.055)/1.055,2.4)},encode:function(e){return e<=.0031308?12.92*e:1.055*r(e,1/2.4)-.055}},s={encode:function(e){return e<.001953125?16*e:r(e,1/1.8)},decode:function(e){return e<16*.001953125?e/16:r(e,1.8)}};function c(e){return{decode:function(t){return i(t)*r(a(t),e)},encode:function(t){return i(t)*r(a(t),1/e)}}}e.default={sRGB:{r:{x:.64,y:.33},g:{x:.3,y:.6},b:{x:.15,y:.06},gamma:o},"Adobe RGB":{r:{x:.64,y:.33},g:{x:.21,y:.71},b:{x:.15,y:.06},gamma:c(2.2)},"Wide Gamut RGB":{r:{x:.7347,y:.2653},g:{x:.1152,y:.8264},b:{x:.1566,y:.0177},gamma:c(563/256)},"ProPhoto RGB":{r:{x:.7347,y:.2653},g:{x:.1596,y:.8404},b:{x:.0366,y:1e-4},gamma:s}},t.exports=e.default})),Ur=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});function t(e){return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function n(e){return e[0][0]*(e[2][2]*e[1][1]-e[2][1]*e[1][2])+e[1][0]*(e[2][1]*e[0][2]-e[2][2]*e[0][1])+e[2][0]*(e[1][2]*e[0][1]-e[1][1]*e[0][2])}function r(e){var t=1/n(e);return[[(e[2][2]*e[1][1]-e[2][1]*e[1][2])*t,(e[2][1]*e[0][2]-e[2][2]*e[0][1])*t,(e[1][2]*e[0][1]-e[1][1]*e[0][2])*t],[(e[2][0]*e[1][2]-e[2][2]*e[1][0])*t,(e[2][2]*e[0][0]-e[2][0]*e[0][2])*t,(e[1][0]*e[0][2]-e[1][2]*e[0][0])*t],[(e[2][1]*e[1][0]-e[2][0]*e[1][1])*t,(e[2][0]*e[0][1]-e[2][1]*e[0][0])*t,(e[1][1]*e[0][0]-e[1][0]*e[0][1])*t]]}function i(e,t){return[e[0][0]*t[0]+e[0][1]*t[1]+e[0][2]*t[2],e[1][0]*t[0]+e[1][1]*t[1]+e[1][2]*t[2],e[2][0]*t[0]+e[2][1]*t[1]+e[2][2]*t[2]]}function a(e,t){return[[e[0][0]*t[0],e[0][1]*t[1],e[0][2]*t[2]],[e[1][0]*t[0],e[1][1]*t[1],e[1][2]*t[2]],[e[2][0]*t[0],e[2][1]*t[1],e[2][2]*t[2]]]}function o(e,t){return[[e[0][0]*t[0][0]+e[0][1]*t[1][0]+e[0][2]*t[2][0],e[0][0]*t[0][1]+e[0][1]*t[1][1]+e[0][2]*t[2][1],e[0][0]*t[0][2]+e[0][1]*t[1][2]+e[0][2]*t[2][2]],[e[1][0]*t[0][0]+e[1][1]*t[1][0]+e[1][2]*t[2][0],e[1][0]*t[0][1]+e[1][1]*t[1][1]+e[1][2]*t[2][1],e[1][0]*t[0][2]+e[1][1]*t[1][2]+e[1][2]*t[2][2]],[e[2][0]*t[0][0]+e[2][1]*t[1][0]+e[2][2]*t[2][0],e[2][0]*t[0][1]+e[2][1]*t[1][1]+e[2][2]*t[2][1],e[2][0]*t[0][2]+e[2][1]*t[1][2]+e[2][2]*t[2][2]]]}e.transpose=t,e.determinant=n,e.inverse=r,e.multiply=i,e.scalar=a,e.product=o})),Wr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.PI;function n(e){for(var n=e*180/t;n<0;)n+=360;for(;n>360;)n-=360;return n}function r(e){for(var n=t*e/180;n<0;)n+=2*t;for(;n>2*t;)n-=2*t;return n}e.fromRadian=n,e.toRadian=r})),Gr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.round;function n(e){return e[0]==`#`&&(e=e.slice(1)),e.length<6&&(e=e.split(``).map(function(e){return e+e}).join(``)),e.match(/../g).map(function(e){return parseInt(e,16)/255})}function r(e){return`#`+e.map(function(e){return e=t(255*e).toString(16),e.length<2&&(e=`0`+e),e}).join(``)}e.fromHex=n,e.toHex=r})),Kr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=o(Ur()),r=a(Vr()),i=a(Hr());function a(e){return e&&e.__esModule?e:{default:e}}function o(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function s(){var e=arguments.length<=0||arguments[0]===void 0?i.default.sRGB:arguments[0],t=arguments.length<=1||arguments[1]===void 0?r.default.D65:arguments[1],a=[e.r,e.g,e.b],o=n.transpose(a.map(function(e){return[e.x/e.y,1,(1-e.x-e.y)/e.y]})),s=e.gamma,c=n.multiply(n.inverse(o),t),l=n.scalar(o,c),u=n.inverse(l);return{fromRgb:function(e){return n.multiply(l,e.map(s.decode))},toRgb:function(e){return n.multiply(u,e).map(s.encode)}}}e.default=s,t.exports=e.default})),qr=s(((e,t)=>{t.exports={illuminant:Vr(),workspace:Hr(),matrix:Ur(),degree:Wr(),rgb:Gr(),xyz:Kr()}})),Jr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.cfs=e.distance=e.lerp=e.corLerp=void 0;var t=Br();function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);ti/2&&(e>t?t+=i:e+=i),((1-n)*e+n*t)%(i||1/0)}function u(e,t,n){var r={};for(var i in e)r[i]=l(e[i],t[i],n,i);return r}function d(e,t){var n=0;for(var r in e)n+=o(e[r]-t[r],2);return s(n)}function f(e){return t.merge.apply(void 0,r(e.split(``).map(function(e){return n({},e,!0)})))}e.corLerp=l,e.lerp=u,e.distance=d,e.cfs=f})),Yr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=Jr();function a(e,t){var a=arguments.length<=2||arguments[2]===void 0?1e-6:arguments[2],o=-a,s=1+a,c=Math,l=c.min,u=c.max,d=n([`000`,`fff`].map(function(n){return t.fromXyz(e.fromRgb(r.rgb.fromHex(n)))}),2),f=d[0],p=d[1];function m(n){var r=e.toRgb(t.toXyz(n));return[r.map(function(e){return e>=o&&e<=s}).reduce(function(e,t){return e&&t},!0),r]}function h(e,t){for(var r=arguments.length<=2||arguments[2]===void 0?.001:arguments[2];(0,i.distance)(e,t)>r;){var a=(0,i.lerp)(e,t,.5);n(m(a),1)[0]?e=a:t=a}return e}function g(e){return(0,i.lerp)(f,p,e)}function _(e){return e.map(function(e){return u(o,l(s,e))})}return{contains:m,limit:h,spine:g,crop:_}}e.default=a,t.exports=e.default})),Xr=s((e=>{var t=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0}),e.toNotation=e.fromNotation=e.toHue=e.fromHue=void 0;var n=Jr(),r=Math.floor,i=[{s:`R`,h:20.14,e:.8,H:0},{s:`Y`,h:90,e:.7,H:100},{s:`G`,h:164.25,e:1,H:200},{s:`B`,h:237.53,e:1.2,H:300},{s:`R`,h:380.14,e:.8,H:400}],a=i.map(function(e){return e.s}).slice(0,-1).join(``);function o(e){e50){var o=[n,t];t=o[0],n=o[1],i=100-i}return i<1?a[t]:a[t]+i.toFixed()+a[n]}e.fromHue=o,e.toHue=s,e.fromNotation=l,e.toNotation=u})),Zr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=s(Xr()),a=Jr(),o=Br();function s(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var c=Math,l=c.pow,u=c.sqrt,d=c.exp,f=c.abs,p=c.sign,m=Math,h=m.sin,g=m.cos,_=m.atan2,v={average:{F:1,c:.69,N_c:1},dim:{F:.9,c:.59,N_c:.9},dark:{F:.8,c:.535,N_c:.8}},y=[[.7328,.4296,-.1624],[-.7036,1.6975,.0061],[.003,.0136,.9834]],b=[[.38971,.68898,-.07868],[-.22981,1.1834,.04641],[0,0,1]],x=y,S=r.matrix.inverse(y),C=r.matrix.product(b,r.matrix.inverse(y)),w=r.matrix.product(y,r.matrix.inverse(b)),T={whitePoint:r.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ee=(0,a.cfs)(`QJMCshH`),E=(0,a.cfs)(`JCh`);function D(){var e=arguments.length<=0||arguments[0]===void 0?{}:arguments[0],t=arguments.length<=1||arguments[1]===void 0?ee:arguments[1];e=(0,o.merge)(T,e);var a=e.whitePoint,s=e.adaptingLuminance,c=e.backgroundLuminance,m=v[e.surroundType],b=m.F,D=m.c,O=m.N_c,te=a[1],k=1/(5*s+1),A=.2*l(k,4)*5*s+.1*l(1-l(k,4),2)*l(5*s,1/3),ne=c/te,re=.725*l(1/ne,.2),ie=re,ae=1.48+u(ne),oe=e.discounting?1:b*(1-1/3.6*d(-(s+42)/92)),j=n(r.matrix.multiply(y,a).map(function(e){return oe*te/e+1-oe}),3),se=j[0],M=j[1],ce=j[2],N=pe(de(le(a)));function le(e){var t=n(r.matrix.multiply(x,e),3),i=t[0],a=t[1],o=t[2];return[se*i,M*a,ce*o]}function ue(e){var t=n(e,3),i=t[0],a=t[1],o=t[2];return r.matrix.multiply(S,[i/se,a/M,o/ce])}function de(e){return r.matrix.multiply(C,e).map(function(e){var t=l(A*f(e)/100,.42);return p(e)*400*t/(27.13+t)+.1})}function fe(e){return r.matrix.multiply(w,e.map(function(e){var t=e-.1;return p(t)*100/A*l(27.13*f(t)/(400-f(t)),1/.42)}))}function pe(e){var t=n(e,3),r=t[0],i=t[1],a=t[2];return(r*2+i+a/20-.305)*re}function me(e){return 4/D*u(e/100)*(N+4)*l(A,.25)}function he(e){return 6.25*l(D*e/((N+4)*l(A,.25)),2)}function ge(e){return e*l(A,.25)}function _e(e,t){return l(e/100,2)*t/l(A,.25)}function ve(e){return e/l(A,.25)}function ye(e,t){return 100*u(e/t)}function be(e,t){var n=t.Q,r=t.J,a=t.M,o=t.C,s=t.s,c=t.h,l=t.H,u={};return e.J&&(u.J=isNaN(r)?he(n):r),e.C&&(isNaN(o)?isNaN(a)?(n=isNaN(n)?me(r):n,u.C=_e(s,n)):u.C=ve(a):u.C=t.C),e.h&&(u.h=isNaN(c)?i.toHue(l):c),e.Q&&(u.Q=isNaN(n)?me(r):n),e.M&&(u.M=isNaN(a)?ge(o):a),e.s&&(isNaN(s)?(n=isNaN(n)?me(r):n,a=isNaN(a)?ge(o):a,u.s=ye(a,n)):u.s=s),e.H&&(u.H=isNaN(l)?i.fromHue(c):l),u}function P(e){var i=de(le(e)),a=n(i,3),o=a[0],s=a[1],c=a[2],d=o-s*12/11+c/11,f=(o+s-2*c)/9,p=_(f,d),m=r.degree.fromRadian(p),h=1/4*(g(p+2)+3.8),v=100*l(pe(i)/N,D*ae);return be(t,{J:v,C:l(5e4/13*O*ie*h*u(d*d+f*f)/(o+s+21/20*c),.9)*u(v/100)*l(1.64-l(.29,ne),.73),h:m})}function F(e){var t=be(E,e),n=t.J,i=t.C,a=t.h,o=r.degree.toRadian(a),s=l(i/(u(n/100)*l(1.64-l(.29,ne),.73)),10/9),c=1/4*(g(o+2)+3.8),d=N*l(n/100,1/D/ae),p=5e4/13*O*ie*c/s,m=d/re+.305,_=m*61/20*460/1403,v=61/20*220/1403,y=21/20*6300/1403-27/1403,b=h(o),x=g(o),S,C;return s===0||isNaN(s)?S=C=0:f(b)>=f(x)?(C=_/(p/b+v*x/b+y),S=C*x/b):(S=_/(p/x+v+y*b/x),C=S*b/x),ue(fe([20/61*m+451/1403*S+288/1403*C,20/61*m-891/1403*S-261/1403*C,20/61*m-220/1403*S-6300/1403*C]))}return{fromXyz:P,toXyz:F,fillOut:be}}e.default=D,t.exports=e.default})),Qr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=qr(),r=Math,i=r.sqrt,a=r.pow,o=r.exp,s=r.log,c=r.cos,l=r.sin,u=r.atan2,d={LCD:{K_L:.77,c_1:.007,c_2:.0053},SCD:{K_L:1.24,c_1:.007,c_2:.0363},UCS:{K_L:1,c_1:.007,c_2:.0228}};function f(){var e=d[arguments.length<=0||arguments[0]===void 0?`UCS`:arguments[0]],t=e.K_L,r=e.c_1,f=e.c_2;function p(e){var t=e.J,i=e.M,a=e.h,o=n.degree.toRadian(a),u=(1+100*r)*t/(1+r*t),d=1/f*s(1+f*i);return{J_p:u,a_p:d*c(o),b_p:d*l(o)}}function m(e){var t=e.J_p,s=e.a_p,c=e.b_p,l=-t/(r*t-100*r-1),d=(o(f*i(a(s,2)+a(c,2)))-1)/f,p=u(c,s);return{J:l,M:d,h:n.degree.fromRadian(p)}}function h(e,n){return i(a((e.J_p-n.J_p)/t,2)+a(e.a_p-n.a_p,2)+a(e.b_p-n.b_p,2))}return{fromCam:p,toCam:m,distance:h}}e.default=f,t.exports=e.default})),$r=s(((e,t)=>{var n=Jr(),r=Yr(),i=Zr(),a=Qr(),o=Xr();t.exports={gamut:r,cfs:n.cfs,lerp:n.lerp,cam:i,ucs:a,hq:o}})),ei=l(qr(),1),ti=l($r(),1);function ni(e){let t=new q;return t.rgb_r=e[0],t.rgb_g=e[1],t.rgb_b=e[2],t.rgbToHsluv(),[t.hsluv_h,t.hsluv_s,t.hsluv_l]}function ri(e){let t=new q;return t.hsluv_h=e[0],t.hsluv_s=e[1],t.hsluv_l=e[2],t.hsluvToRgb(),[t.rgb_r,t.rgb_g,t.rgb_b]}var ii=ti.default.cam({whitePoint:ei.default.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ti.default.cfs(`JCh`)),ai=ei.default.xyz(ei.default.workspace.sRGB,ei.default.illuminant.D65),oi=e=>ai.toRgb(ii.toXyz({J:e[0],C:e[1],h:e[2]})),si=e=>{let t=ii.fromXyz(ai.fromRgb(e));return[t.J,t.C,t.h]},[ci,li]=(()=>{let e={k_l:1,c1:.007,c2:.0228},t=Math.PI,n=64/t/5,r=1/(5*n+1),i=.2*r**4*(5*n)+.1*(1-r**4)**2*(5*n)**(1/3);return[n=>{let[r,a,o]=n,s=a*i**.25,c=(1+100*e.c1)*r/(1+e.c1*r);c/=e.k_l;let l=1/e.c2*Math.log(1+e.c2*s),u=l*Math.cos(t/180*o),d=l*Math.sin(t/180*o);return[c,u,d]},n=>{let[r,a,o]=n,s=Math.sqrt(a*a+o*o),c=(Math.exp(s*e.c2)-1)/e.c2,l=(180/t*Math.atan2(o,a)+360)%360,u=c/i**.25;return[r/(1+e.c1*(100-r)),u,l]}]})(),ui=e=>oi(li(e)),di=e=>ci(si(e)),fi=console;fi.color=(e,t=``)=>{let n=K(e).luminance();fi.log(`%c${e} ${t}`,`background-color: ${e};padding: 5px; border-radius: 5px; color: ${n>.5?`#000`:`#fff`}`)},fi.ramp=(e,t=1)=>{fi.log(`%c `,`font-size: 1px;line-height: 16px;background: ${K.getCSSGradient(e,t)};padding: 0 0 0 200px; border-radius: 2px;`)};var pi=(e,t,n,r,i,a,o=.1)=>{if(e===n||t===r)return!0;let s=(r-t)/(n-e),c=(a+i/s-t+s*e)/(s+1/s),l=a+i/s-c/s;return(i-c)**2+(a-l)**2{let i=(t[0]+n[0])/2,a=e(i);return pi(...t,...n,i,a,r)?null:[i,a]},hi=(e,t,n,r=.1)=>{let i=(n-t)/10,a=[];for(let r=t;rMath.round(e*10**t)/10**t,_i=(e,t=1,n=90,r=.005)=>{let i=hi(t=>e(t).gl()[0],0,t,r),a=hi(t=>e(t).gl()[1],0,t,r),o=hi(t=>e(t).gl()[2],0,t,r);return`linear-gradient(${n}deg, ${Array.from(new Set([...i.map(e=>gi(e[0])),...a.map(e=>gi(e[0])),...o.map(e=>gi(e[0]))].sort((e,t)=>e-t))).map(t=>`${e(t).hex()} ${gi(t*100)}%`).join()});`},vi=e=>{e.Color.prototype.jch=function(){return si(this._rgb.slice(0,3).map(e=>e/255))},e.jch=(...t)=>new e.Color(...oi(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.jab=function(){return di(this._rgb.slice(0,3).map(e=>e/255))},e.jab=(...t)=>new e.Color(...ui(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.hsluv=function(){return ni(this._rgb.slice(0,3).map(e=>e/255))},e.hsluv=(...t)=>new e.Color(...ri(t).map(e=>Math.floor(e*255)),`rgb`);let t=e.interpolate,n={jch:si,jab:di,hsluv:ni},r=(e,t,n)=>(Math.abs(e-t)>360/2&&(e>t?t+=360:e+=360),((1-n)*e+n*t)%360);e.interpolate=(i,a,o=.5,s=`lrgb`)=>{if(n[s]){typeof i!=`object`&&(i=new e.Color(i)),typeof a!=`object`&&(a=new e.Color(a));let t=n[s](i.gl()),c=n[s](a.gl()),l=Number.isNaN(i.hsl()[0]),u=Number.isNaN(a.hsl()[0]),d,f,p;switch(s){case`hsluv`:t[1]<1e-10&&(t[0]=c[0]),t[1]===0&&(t[1]=c[1]),c[1]<1e-10&&(c[0]=t[0]),c[1]===0&&(c[1]=t[1]),d=r(t[0],c[0],o),f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o;break;case`jch`:l&&(t[2]=c[2]),u&&(c[2]=t[2]),d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=r(t[2],c[2],o);break;default:d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o}return e[s](d,f,p).alpha(i.alpha()+o*(a.alpha()-i.alpha()))}return t(i,a,o,s)},e.getCSSGradient=_i},X={mainTRC:2.4,get mainTRCencode(){return 1/this.mainTRC},sRco:.2126729,sGco:.7151522,sBco:.072175,normBG:.56,normTXT:.57,revTXT:.62,revBG:.65,blkThrs:.022,blkClmp:1.414,scaleBoW:1.14,scaleWoB:1.14,loBoWoffset:.027,loWoBoffset:.027,deltaYmin:5e-4,loClip:.1,mFactor:1.9468554433171,get mFactInv(){return 1/this.mFactor},mOffsetIn:.0387393816571401,mExpAdj:.283343396420869,get mExp(){return this.mExpAdj/this.blkClmp},mOffsetOut:.312865795870758};function yi(e,t,n=-1){let r=[0,1.1];if(isNaN(e)||isNaN(t)||Math.min(e,t)r[1])return 0;let i=0,a=0,o=`BoW`;return e=e>X.blkThrs?e:e+(X.blkThrs-e)**+X.blkClmp,t=t>X.blkThrs?t:t+(X.blkThrs-t)**+X.blkClmp,Math.abs(t-e)e?(i=(t**+X.normBG-e**+X.normTXT)*X.scaleBoW,a=i-X.loClip?0:i+X.loWoBoffset),n<0?a*100:n==0?Math.round(Math.abs(a)*100)+``+o+``:Number.isInteger(n)?(a*100).toFixed(n):0)}function bi(e=[0,0,0]){function t(e){return(e/255)**X.mainTRC}return X.sRco*t(e[0])+X.sGco*t(e[1])+X.sBco*t(e[2])}var xi=(e,t,n,r,i,a,o,s,c)=>{let l=1-c,u=l*l,d=u*l,f=c*c*c;return{x:d*e+u*3*c*n+l*3*c*c*i+f*o,y:d*t+u*3*c*r+l*3*c*c*a+f*s}},Si=(e,t)=>{let n=[],r={x:+e[0],y:+e[1]};for(let i=0,a=e.length;a-2*!t>i;i+=2){let o=[{x:+e[i-2],y:+e[i-1]},{x:+e[i],y:+e[i+1]},{x:+e[i+2],y:+e[i+3]},{x:+e[i+4],y:+e[i+5]}];t?i?a-4===i?o[3]={x:+e[0],y:+e[1]}:a-2===i&&(o[2]={x:+e[0],y:+e[1]},o[3]={x:+e[2],y:+e[3]}):o[0]={x:+e[a-2],y:+e[a-1]}:a-4===i?o[3]=o[2]:i||(o[0]={x:+e[i],y:+e[i+1]}),n.push([r.x,r.y,(-o[0].x+6*o[1].x+o[2].x)/6,(-o[0].y+6*o[1].y+o[2].y)/6,(o[1].x+6*o[2].x-o[3].x)/6,(o[1].y+6*o[2].y-o[3].y)/6,o[2].x,o[2].y]),r=o[2]}return n},Ci=(e,t,n,r,i,a,o,s)=>{let c=e,l=t,u=0;for(let d=1;d<5;d++){let{x:f,y:p}=xi(e,t,n,r,i,a,o,s,d/5);u+=Math.hypot(f-c,p-l),c=f,l=p}return u+=Math.hypot(o-c,s-l),u},wi=(e,t,n,r,i,a,o,s)=>{let c=Math.floor(Ci(e,t,n,r,i,a,o,s)*.75),l=[],u=0;for(let d=0;d<=c;d++){let f=xi(e,t,n,r,i,a,o,s,d/c),p=Math.round(f.x);if(l[p]=f.y,p-u>1){let e=l[u],t=l[p];for(let n=u+1;nl[Math.round(e)]||null},Ti={CAM02:`jab`,CAM02p:`jch`,HEX:`hex`,HSL:`hsl`,HSLuv:`hsluv`,HSV:`hsv`,LAB:`lab`,LCH:`lch`,RGB:`rgb`,OKLAB:`oklab`,OKLCH:`oklch`};function Z(e,t=0){let n=10**t;return Math.round(e*n)/n}function Ei(e,t){let n;return n=e>1?(e-1)*t+1:e<-1?(e+1)*t-1:1,Z(n,2)}function Di(e){return K(String(e)).jch()}function Oi(e){return K(String(e)).hsluv()}function ki(e,t,n){let r=[[],[],[]];if(e.forEach((e,n)=>r.forEach((r,i)=>r.push(t[n],e[i]))),n===`hcl`){let e=r[1];for(let t=1;t{let t=[];for(let n=1;n{e[t]=e[n]}),t.length=0;break}if(t.length){let n=K(`#ccc`).jch()[2];t.forEach(t=>{e[t]=n})}t.length=0;for(let n=e.length-1;n>0;n-=2)if(Number.isNaN(e[n]))t.push(n);else{t.forEach(t=>{e[t]=e[n]});break}for(let t=1;tSi(e).map(e=>wi(...e)));return e=>{let t=i.map(t=>{for(let n=0;nr*t**e+i}function ji({swatches:e,colorKeys:t,colorspace:n,colorSpace:r=n??`LAB`,shift:i=1,fullScale:a=!0,smooth:o=!1,distributeLightness:s=`linear`,sortColor:c=!0,asFun:l=!1}={}){n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead.");let u=Ti[r];if(!u)throw Error(`Colorspace “${r}” not supported`);if(!t)throw Error(`Colorkeys missing: returned “${t}”`);let d;if(a)d=t.map(t=>e-e*(K(t).jch()[0]/100)).sort((e,t)=>e-t).concat(e),d.unshift(0);else{let n=t.map(e=>K(e).jch()[0]/100),r=Math.min(...n),i=Math.max(...n);d=n.map(t=>t===0||isNaN((t-r)/(i-r))?0:e-(t-r)/(i-r)*e).sort((e,t)=>e-t)}let f=Ai(i,[1,e],[1,e]);if(f=d.map(e=>Math.max(0,f(e))),d=f,s===`polynomial`){let t=e=>Math.sqrt(Math.sqrt((e**2.25+e**4)/2));d=f.map(t=>t/e).map(n=>t(n)*e)}let p=t.map((e,t)=>({colorKeys:Di(e),index:t})).sort((e,t)=>t.colorKeys[0]-e.colorKeys[0]).map(e=>t[e.index]),m=[],h;if(a){let e=u===`lch`?K.lch(...K(`#fff`).lch()):`#ffffff`,t=u===`lch`?K.lch(...K(`#000`).lch()):`#000000`;m=[e,...p,t]}else m=c?p:t;let g;if(o){let t=m;if(m=m.map(e=>K(String(e))[u]()),u===`hcl`&&m.forEach(e=>{e[1]=Number.isNaN(e[1])?0:e[1]}),u===`jch`)for(let e=0;eh(t))}else h=K.scale(m.map(e=>typeof e==`object`&&e.constructor===K.Color?e:String(e))).domain(d).mode(u);return l?h:(!o||o===!1?h.colors(e):g).filter(e=>e!=null)}function Mi(e,t){let n=[],r={};return Object.keys(e).forEach(n=>{r[e[n][t]]=e[n]}),Object.keys(r).forEach(e=>n.push(r[e])),n}function Ni(e){return Number.isNaN(e)?0:e}function Pi(e,t,n=!1){if(!e)throw Error(`Cannot convert color value of “${e}”`);if(!Ti[t])throw Error(`Cannot convert to colorspace “${t}”`);let r=Ti[t],i=K(String(e))[r]();if(t===`HSL`&&i.pop(),t===`HEX`){if(n){let t=K(String(e)).rgb();return{r:t[0],g:t[1],b:t[2]}}return i}let a={},o=i.map(Ni);o=o.map((e,t)=>{let i=Z(e),o=t;r===`hsluv`&&(o+=2);let s=r.charAt(o);return r===`jch`&&s===`c`&&(s=`C`),a[s===`j`?`J`:s]=i,r in{lab:1,lch:1,jab:1,jch:1}?n||(s===`l`||s===`j`)&&(i+=`%`):r!==`hsluv`&&(s===`s`||s===`l`||s===`v`)&&(a[s]=Z(e,2),n||(i=Z(e*100),i+=`%`)),i});let s=`${r}(${o.join(`, `)})`;return n?a:s}function Fi(e,t,n){let r=[e,t,n].map(e=>(e/=255,e<=.03928?e/12.92:((e+.055)/1.055)**2.4));return r[0]*.2126+r[1]*.7152+r[2]*.0722}function Ii(e,t,n,r=`wcag2`){if(n===void 0){let e=K.rgb(...t).hsluv()[2];n=Z(e/100,2)}if(r===`wcag2`){let r=Fi(e[0],e[1],e[2]),i=Fi(t[0],t[1],t[2]),a=(r+.05)/(i+.05),o=(i+.05)/(r+.05);return n<.5?a>=1?a:-o:a<1?o:a===1?a:-a}else if(r===`wcag3`)return n<.5?yi(bi(e),bi(t))*-1:yi(bi(e),bi(t));else throw Error(`Contrast calculation method ${r} unsupported; use 'wcag2' or 'wcag3'`)}function Li(e,t){if(!e)throw Error(`Array undefined`);if(!Array.isArray(e))throw Error(`Passed object is not an array`);let n=t===`wcag2`?0:1;return Math.min(...e.filter(e=>e>=n))}function Ri(e,t){if(!e)throw Error(`Ratios undefined`);e=e.sort((e,t)=>e-t);let n=Li(e,t),r=e.indexOf(n),i=[],a=e.slice(0,r),o=e.slice(r,e.length);for(let e=0;ee-t),i}var zi=(e,t,n,r,i)=>{let a=3e3,o=ji({swatches:a,colorKeys:e._modifiedKeys,colorspace:e._colorspace,shift:1,smooth:e._smooth,asFun:!0}),s={},c=e=>{if(s[e])return s[e];let r=Ii(K(o(e)).rgb(),t,n,i);return s[e]=r,r},l=e=>{let t=c(0).01&&o;)o--,n/=2,iu.push(o(l(+e)))),u},Q=class{constructor({name:e,colorKeys:t,colorspace:n,colorSpace:r=n??`RGB`,ratios:i,smooth:a=!1,output:o=`HEX`,saturation:s=100}){if(n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),this._name=e,this._colorKeys=t,this._modifiedKeys=t,this._colorspace=r,this._ratios=i,this._smooth=a,this._output=o,this._saturation=s,!this._name)throw Error(`Color missing name`);if(!this._colorKeys)throw Error(`Color Keys are undefined`);if(!Ti[this._colorspace])throw Error(`Colorspace “${r}” not supported`);if(!Ti[this._output])throw Error(`Output “${this._output}” not supported`);for(let e=0;e{let n=K(`${t}`).oklch(),r=n[1]*(this._saturation/100),i=K.oklch(n[0],r,n[2]),a=K.rgb(i).hex();e.push(a)}),this._modifiedKeys=e,this._generateColorScale()}_generateColorScale(){this._colorScale=ji({swatches:3e3,colorKeys:this._modifiedKeys,colorSpace:this._colorspace,shift:1,smooth:this._smooth,asFun:!0})}},Bi=class extends Q{get backgroundColorScale(){return this._backgroundColorScale||this._generateColorScale(),this._backgroundColorScale}_generateColorScale(){Q.prototype._generateColorScale.call(this);let e=ji({swatches:1e3,colorKeys:this._colorKeys,colorspace:this._colorspace,shift:1,smooth:this._smooth});e.push(...this.colorKeys);let t=Mi(e.map((e,t)=>({value:Math.round(Oi(e)[2]),index:t})),`value`).map(t=>e[t.index]);return t.length>=101&&(t.length=100,t.push(`#ffffff`)),this._backgroundColorScale=t.map(e=>Pi(e,this._output)),this._backgroundColorScale}},Vi=class{constructor({colors:e,backgroundColor:t,lightness:n,contrast:r=1,saturation:i=100,output:a=`HEX`,formula:o=`wcag2`}){if(this._output=a,this._colors=e,this._lightness=n,this._saturation=i,this._formula=o,this._setBackgroundColor(t),this._setBackgroundColorValue(),this._contrast=r,!this._colors)throw Error(`No colors are defined`);if(!this._backgroundColor)throw Error(`Background color is undefined`);if(e.forEach(e=>{if(!e.ratios)throw Error(`Color ${e.name}'s ratios are undefined`)}),!Ti[this._output])throw Error(`Output “${a}” not supported`);this._saturation<100&&this._updateColorSaturation(this._saturation),this._findContrastColors(),this._findContrastColorPairs(),this._findContrastColorValues()}set formula(e){this._formula=e,this._findContrastColors()}get formula(){return this._formula}set contrast(e){this._contrast=e,this._findContrastColors()}get contrast(){return this._contrast}set lightness(e){this._lightness=e,this._setBackgroundColor(this._backgroundColor),this._findContrastColors()}get lightness(){return this._lightness}set saturation(e){this._saturation=e,this._updateColorSaturation(e),this._findContrastColors()}get saturation(){return this._saturation}set backgroundColor(e){this._setBackgroundColor(e),this._findContrastColors()}get backgroundColorValue(){return this._backgroundColorValue}get backgroundColor(){return this._backgroundColor}set colors(e){this._colors=e,this._findContrastColors()}get colors(){return this._colors}set addColor(e){this._colors.push(e),this._findContrastColors()}set removeColor(e){this._colors=this._colors.filter(t=>t.name!==e.name),this._findContrastColors()}set updateColor(e){if(Array.isArray(e))for(let t=0;tn.name===e[t].color);n=n[0];let r=this._colors.indexOf(n),i=this._colors.filter(n=>n.name!==e[t].color);e[t].name&&(n.name=e[t].name),e[t].colorKeys&&(n.colorKeys=e[t].colorKeys),e[t].ratios&&(n.ratios=e[t].ratios),(e[t].colorSpace!==void 0||e[t].colorspace!==void 0)&&(e[t].colorspace!==void 0&&e[t].colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),n.colorSpace=e[t].colorSpace??e[t].colorspace),e[t].smooth&&(n.smooth=e[t].smooth),n._generateColorScale(),i.splice(r,0,n),this._colors=i}else{let t=this._colors.filter(t=>t.name===e.color);t=t[0];let n=this._colors.indexOf(t),r=this._colors.filter(t=>t.name!==e.color);e.name&&(t.name=e.name),e.colorKeys&&(t.colorKeys=e.colorKeys),e.ratios&&(t.ratios=e.ratios),(e.colorSpace!==void 0||e.colorspace!==void 0)&&(e.colorspace!==void 0&&e.colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),t.colorSpace=e.colorSpace??e.colorspace),e.smooth&&(t.smooth=e.smooth),t._generateColorScale(),r.splice(n,0,t),this._colors=r}this._findContrastColors()}set output(e){this._output=e,this._colors.forEach(e=>{e.output=this._output}),this._backgroundColor.output=this._output,this._findContrastColors()}get output(){return this._output}get contrastColors(){return this._contrastColors}get contrastColorPairs(){return this._contrastColorPairs}get contrastColorValues(){return this._contrastColorValues}_setBackgroundColor(e){if(typeof e==`string`){let t=new Bi({name:`background`,colorKeys:[e],output:`RGB`}),n=Z(K(String(e)).hsluv()[2]);this._backgroundColor=t,this._lightness=n,this._backgroundColorValue=t[this._lightness]}else{e.output=`RGB`;let t=e.backgroundColorScale[this._lightness];this._backgroundColor=e,this._backgroundColorValue=t}}_setBackgroundColorValue(){this._backgroundColorValue=this._backgroundColor.backgroundColorScale[this._lightness]}_updateColorSaturation(e){this._colors.map(t=>{t.saturation=e})}_findContrastColors(){let e=K(String(this._backgroundColorValue)).rgb(),t=this._lightness/100,n={background:Pi(this._backgroundColorValue,this._output)},r=[],i=[],a={...n};return r.push(n),this._colors.map(n=>{if(n.ratios!==void 0){let o,s=[],c={name:n.name,values:s},l;Array.isArray(n.ratios)?l=n.ratios:Array.isArray(n.ratios)||(o=Object.keys(n.ratios),l=Object.values(n.ratios)),l=l.map(e=>Ei(+e,this._contrast));let u=zi(n,e,t,l,this._formula).map(e=>Pi(e,this._output));for(let e=0;e{let t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return[Number.parseInt(t[1],16),Number.parseInt(t[2],16),Number.parseInt(t[3],16)]},Wi=(e,t,n)=>{let r=e/255,i=t/255,a=n/255,o=Math.min(r,i,a),s=Math.max(r,i,a),c=s-o,l=0,u=0,d=0;return l=c===0?0:s===r?(i-a)/c%6:s===i?(a-r)/c+2:(r-i)/c+4,l=Math.round(l*60),l<0&&(l+=360),d=(s+o)/2,u=c===0?0:c/(1-Math.abs(2*d-1)),u=+(u*100).toFixed(1),d=+(d*100).toFixed(1),[l,u,Math.round(d)]},Gi=(e,t,n,r)=>{let i=n/100,a=t*Math.min(i,1-i)/100,o=t=>{let n=(t+e/30)%12,r=i-a*Math.max(Math.min(n-3,9-n,1),-1);return Math.round(255*r).toString(16).padStart(2,`0`).toUpperCase()},s=o(0),c=o(8),l=o(4),u=((e,t,n)=>Math.min(Math.max(e,t),n))(r,0,1);return`#${s}${c}${l}${Math.round(u*255).toString(16).padStart(2,`0`).toUpperCase()}`},Ki=(e,t,n=1)=>{let r=Ui(e),i=Ui(t===`white`?`#FFFFFF`:t===`black`?`#000000`:t),a=r.map((e,t)=>[(e-i[t])/(255-i[t]),(e-i[t])/(0-i[t])]),o=Hi(Math.max(...a.flat().filter(e=>/^-?\d+\.?\d*$/.test(e)))),s=r.map((e,t)=>Math.round((e-i[t]+i[t]*o)/o));if(s.includes(NaN)){let e=Wi(r[0],r[1],r[2]);return{h:e[0],s:Math.round(e[1]*n),l:e[2],a:1}}let c=Wi(s[0],s[1],s[2]);return{h:c[0],s:Math.round(c[1]*n),l:c[2],a:o}},qi={backgroundColor:`gray`,colorSpace:`OKLCH`,colorSmoothing:!1,formula:`wcag2`,output:`HEX`,colors:{gray:[$(215,20,90),$(215,8,50),$(215,6,25)],red:[$(358,100,58),$(350,100,30)],orange:[$(32,100,48),$(12,100,30)],yellow:[$(50,100,50),$(25,100,20)],lime:[$(100,68,50),$(115,86,25)],green:[$(163,87,42),$(168,100,25)],cyan:[$(185,80,45),$(200,98,35)],blue:[$(212,98,46),$(222,95,25)],purple:[$(258,94,64),$(265,100,35)],fuchsia:[$(295,56,50),$(285,80,25)],pink:[$(334,90,50),$(330,91,25)]},themes:{light:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16.75],contrast:1,lightness:100,saturation:100},dark:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16],contrast:1,lightness:6,saturation:97},lightHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:100,saturation:100},darkHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:6,saturation:97}}};function $(e,t,n){return K.hsl(e,t/100,n/100).hex()}function Ji(e,t){let n=e.colorSpace,r=e.colorSmoothing,i=e.themes[t].ratios,a=new Bi({name:`gray`,colorKeys:e.colors.gray,colorspace:n,ratios:i,smooth:r}),o=new Q({name:`blue`,colorKeys:e.colors.blue,colorspace:n,ratios:i,smooth:r}),s=new Q({name:`cyan`,colorKeys:e.colors.cyan,colorspace:n,ratios:i,smooth:r}),c=new Q({name:`fuchsia`,colorKeys:e.colors.fuchsia,colorspace:n,ratios:i,smooth:r}),l=new Q({name:`green`,colorKeys:e.colors.green,colorspace:n,ratios:i,smooth:r}),u=new Q({name:`lime`,colorKeys:e.colors.lime,colorspace:n,ratios:i,smooth:r}),d=new Q({name:`orange`,colorKeys:e.colors.orange,colorspace:n,ratios:i,smooth:r}),f=new Q({name:`pink`,colorKeys:e.colors.pink,colorspace:n,ratios:i,smooth:r}),p=new Q({name:`purple`,colorKeys:e.colors.purple,colorspace:n,ratios:i,smooth:r}),m={gray:a,red:new Q({name:`red`,colorKeys:e.colors.red,colorspace:n,ratios:i,smooth:r}),orange:d,yellow:new Q({name:`yellow`,colorKeys:e.colors.yellow,colorspace:n,ratios:i,smooth:r}),lime:u,green:l,cyan:s,blue:o,purple:p,fuchsia:c,pink:f};return e.colors.custom&&(m.custom=new Q({name:`custom`,colorKeys:e.colors.custom,colorspace:n,ratios:i,smooth:r})),new Vi({colors:Object.values(m),backgroundColor:m[e.backgroundColor],contrast:e.themes[t].contrast,lightness:e.themes[t].lightness,saturation:e.themes[t].saturation,output:e.output,formula:e.formula}).contrastColors}function Yi(e){let t={};for(let n of Object.keys(e.themes))t[n]=Ji(e,n);return t}function Xi(e){qi.colors.custom=[e];let t=Yi(qi);return Object.fromEntries(Object.entries(t).map(([e,t])=>{let n=t.find(e=>e&&e.name===`custom`),r=Object.fromEntries(n.values.map(({name:e,value:t})=>[e,t]));for(let[e,n]of Object.entries(r)){let i=Ki(n,t[0].background);r[`alpha${e.charAt(0).toUpperCase()+e.slice(1)}`]=Gi(i.h,i.s,i.l,i.a)}return[e,r]}))}return e.generateCustomColors=Xi,e.generateThemesJson=Yi,e.hslToHex=$,e.leonardoConfig=qi,e})({}); \ No newline at end of file +var CompoundTheme=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;lt[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),{min:u,max:d}=Math,f=(e,t=0,n=1)=>u(d(t,e),n),p=e=>{e._clipped=!1,e._unclipped=e.slice(0);for(let t=0;t<=3;t++)t<3?((e[t]<0||e[t]>255)&&(e._clipped=!0),e[t]=f(e[t],0,255)):t===3&&(e[t]=f(e[t],0,1));return e},m={};for(let e of[`Boolean`,`Number`,`String`,`Function`,`Array`,`Date`,`RegExp`,`Undefined`,`Null`])m[`[object ${e}]`]=e.toLowerCase();function h(e){return m[Object.prototype.toString.call(e)]||`object`}var g=(e,t=null)=>e.length>=3?Array.prototype.slice.call(e):h(e[0])==`object`&&t?t.split(``).filter(t=>e[0][t]!==void 0).map(t=>e[0][t]):e[0].slice(0),_=e=>{if(e.length<2)return null;let t=e.length-1;return h(e[t])==`string`?e[t].toLowerCase():null},{PI:v,min:y,max:b}=Math,x=e=>Math.round(e*100)/100,S=e=>Math.round(e*100)/100,C=v*2,w=v/3,T=v/180,ee=180/v;function E(e){return[...e.slice(0,3).reverse(),...e.slice(3)]}var D={format:{},autodetect:[]},O=class{constructor(...e){let t=this;if(h(e[0])===`object`&&e[0].constructor&&e[0].constructor===this.constructor)return e[0];let n=_(e),r=!1;if(!n){r=!0,D.sorted||=(D.autodetect=D.autodetect.sort((e,t)=>t.p-e.p),!0);for(let t of D.autodetect)if(n=t.test(...e),n)break}if(D.format[n])t._rgb=p(D.format[n].apply(null,r?e:e.slice(0,-1)));else throw Error(`unknown format: `+e);t._rgb.length===3&&t._rgb.push(1)}toString(){return h(this.hex)==`function`?this.hex():`[${this._rgb.join(`,`)}]`}},te=`3.2.0`,k=(...e)=>new O(...e);k.version=te;var A={aliceblue:`#f0f8ff`,antiquewhite:`#faebd7`,aqua:`#00ffff`,aquamarine:`#7fffd4`,azure:`#f0ffff`,beige:`#f5f5dc`,bisque:`#ffe4c4`,black:`#000000`,blanchedalmond:`#ffebcd`,blue:`#0000ff`,blueviolet:`#8a2be2`,brown:`#a52a2a`,burlywood:`#deb887`,cadetblue:`#5f9ea0`,chartreuse:`#7fff00`,chocolate:`#d2691e`,coral:`#ff7f50`,cornflowerblue:`#6495ed`,cornsilk:`#fff8dc`,crimson:`#dc143c`,cyan:`#00ffff`,darkblue:`#00008b`,darkcyan:`#008b8b`,darkgoldenrod:`#b8860b`,darkgray:`#a9a9a9`,darkgreen:`#006400`,darkgrey:`#a9a9a9`,darkkhaki:`#bdb76b`,darkmagenta:`#8b008b`,darkolivegreen:`#556b2f`,darkorange:`#ff8c00`,darkorchid:`#9932cc`,darkred:`#8b0000`,darksalmon:`#e9967a`,darkseagreen:`#8fbc8f`,darkslateblue:`#483d8b`,darkslategray:`#2f4f4f`,darkslategrey:`#2f4f4f`,darkturquoise:`#00ced1`,darkviolet:`#9400d3`,deeppink:`#ff1493`,deepskyblue:`#00bfff`,dimgray:`#696969`,dimgrey:`#696969`,dodgerblue:`#1e90ff`,firebrick:`#b22222`,floralwhite:`#fffaf0`,forestgreen:`#228b22`,fuchsia:`#ff00ff`,gainsboro:`#dcdcdc`,ghostwhite:`#f8f8ff`,gold:`#ffd700`,goldenrod:`#daa520`,gray:`#808080`,green:`#008000`,greenyellow:`#adff2f`,grey:`#808080`,honeydew:`#f0fff0`,hotpink:`#ff69b4`,indianred:`#cd5c5c`,indigo:`#4b0082`,ivory:`#fffff0`,khaki:`#f0e68c`,laserlemon:`#ffff54`,lavender:`#e6e6fa`,lavenderblush:`#fff0f5`,lawngreen:`#7cfc00`,lemonchiffon:`#fffacd`,lightblue:`#add8e6`,lightcoral:`#f08080`,lightcyan:`#e0ffff`,lightgoldenrod:`#fafad2`,lightgoldenrodyellow:`#fafad2`,lightgray:`#d3d3d3`,lightgreen:`#90ee90`,lightgrey:`#d3d3d3`,lightpink:`#ffb6c1`,lightsalmon:`#ffa07a`,lightseagreen:`#20b2aa`,lightskyblue:`#87cefa`,lightslategray:`#778899`,lightslategrey:`#778899`,lightsteelblue:`#b0c4de`,lightyellow:`#ffffe0`,lime:`#00ff00`,limegreen:`#32cd32`,linen:`#faf0e6`,magenta:`#ff00ff`,maroon:`#800000`,maroon2:`#7f0000`,maroon3:`#b03060`,mediumaquamarine:`#66cdaa`,mediumblue:`#0000cd`,mediumorchid:`#ba55d3`,mediumpurple:`#9370db`,mediumseagreen:`#3cb371`,mediumslateblue:`#7b68ee`,mediumspringgreen:`#00fa9a`,mediumturquoise:`#48d1cc`,mediumvioletred:`#c71585`,midnightblue:`#191970`,mintcream:`#f5fffa`,mistyrose:`#ffe4e1`,moccasin:`#ffe4b5`,navajowhite:`#ffdead`,navy:`#000080`,oldlace:`#fdf5e6`,olive:`#808000`,olivedrab:`#6b8e23`,orange:`#ffa500`,orangered:`#ff4500`,orchid:`#da70d6`,palegoldenrod:`#eee8aa`,palegreen:`#98fb98`,paleturquoise:`#afeeee`,palevioletred:`#db7093`,papayawhip:`#ffefd5`,peachpuff:`#ffdab9`,peru:`#cd853f`,pink:`#ffc0cb`,plum:`#dda0dd`,powderblue:`#b0e0e6`,purple:`#800080`,purple2:`#7f007f`,purple3:`#a020f0`,rebeccapurple:`#663399`,red:`#ff0000`,rosybrown:`#bc8f8f`,royalblue:`#4169e1`,saddlebrown:`#8b4513`,salmon:`#fa8072`,sandybrown:`#f4a460`,seagreen:`#2e8b57`,seashell:`#fff5ee`,sienna:`#a0522d`,silver:`#c0c0c0`,skyblue:`#87ceeb`,slateblue:`#6a5acd`,slategray:`#708090`,slategrey:`#708090`,snow:`#fffafa`,springgreen:`#00ff7f`,steelblue:`#4682b4`,tan:`#d2b48c`,teal:`#008080`,thistle:`#d8bfd8`,tomato:`#ff6347`,turquoise:`#40e0d0`,violet:`#ee82ee`,wheat:`#f5deb3`,white:`#ffffff`,whitesmoke:`#f5f5f5`,yellow:`#ffff00`,yellowgreen:`#9acd32`},ne=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,re=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,ie=e=>{if(e.match(ne)){(e.length===4||e.length===7)&&(e=e.substr(1)),e.length===3&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let t=parseInt(e,16);return[t>>16,t>>8&255,t&255,1]}if(e.match(re)){(e.length===5||e.length===9)&&(e=e.substr(1)),e.length===4&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]+e[3]+e[3]);let t=parseInt(e,16);return[t>>24&255,t>>16&255,t>>8&255,Math.round((t&255)/255*100)/100]}throw Error(`unknown hex color: ${e}`)},{round:ae}=Math,oe=(...e)=>{let[t,n,r,i]=g(e,`rgba`),a=_(e)||`auto`;i===void 0&&(i=1),a===`auto`&&(a=i<1?`rgba`:`rgb`),t=ae(t),n=ae(n),r=ae(r);let o=`000000`+(t<<16|n<<8|r).toString(16);o=o.substr(o.length-6);let s=`0`+ae(i*255).toString(16);switch(s=s.substr(s.length-2),a.toLowerCase()){case`rgba`:return`#${o}${s}`;case`argb`:return`#${s}${o}`;default:return`#${o}`}};O.prototype.name=function(){let e=oe(this._rgb,`rgb`);for(let t of Object.keys(A))if(A[t]===e)return t.toLowerCase();return e},D.format.named=e=>{if(e=e.toLowerCase(),A[e])return ie(A[e]);throw Error(`unknown color name: `+e)},D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&A[e.toLowerCase()])return`named`}}),O.prototype.alpha=function(e,t=!1){return e!==void 0&&h(e)===`number`?t?(this._rgb[3]=e,this):new O([this._rgb[0],this._rgb[1],this._rgb[2],e],`rgb`):this._rgb[3]},O.prototype.clipped=function(){return this._rgb._clipped||!1};var j={Kn:18,labWhitePoint:`d65`,Xn:.95047,Yn:1,Zn:1.08883,t0:.137931034,t1:.206896552,t2:.12841855,t3:.008856452,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},se=new Map([[`a`,[1.0985,.35585]],[`b`,[1.0985,.35585]],[`c`,[.98074,1.18232]],[`d50`,[.96422,.82521]],[`d55`,[.95682,.92149]],[`d65`,[.95047,1.08883]],[`e`,[1,1,1]],[`f2`,[.99186,.67393]],[`f7`,[.95041,1.08747]],[`f11`,[1.00962,.6435]],[`icc`,[.96422,.82521]]]);function M(e){let t=se.get(String(e).toLowerCase());if(!t)throw Error(`unknown Lab illuminant `+e);j.labWhitePoint=e,j.Xn=t[0],j.Zn=t[1]}function ce(){return j.labWhitePoint}var N=(...e)=>{e=g(e,`lab`);let[t,n,r]=e,[i,a,o]=le(t,n,r),[s,c,l]=de(i,a,o);return[s,c,l,e.length>3?e[3]:1]},le=(e,t,n)=>{let{kE:r,kK:i,kKE:a,Xn:o,Yn:s,Zn:c}=j,l=(e+16)/116,u=.002*t+l,d=l-.005*n,f=u*u*u,p=d*d*d,m=f>r?f:(116*u-16)/i,h=e>a?((e+16)/116)**3:e/i,g=p>r?p:(116*d-16)/i;return[m*o,h*s,g*c]},ue=e=>{let t=Math.sign(e);return e=Math.abs(e),(e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055)*t},de=(e,t,n)=>{let{MtxAdaptMa:r,MtxAdaptMaI:i,MtxXYZ2RGB:a,RefWhiteRGB:o,Xn:s,Yn:c,Zn:l}=j,u=s*r.m00+c*r.m10+l*r.m20,d=s*r.m01+c*r.m11+l*r.m21,f=s*r.m02+c*r.m12+l*r.m22,p=o.X*r.m00+o.Y*r.m10+o.Z*r.m20,m=o.X*r.m01+o.Y*r.m11+o.Z*r.m21,h=o.X*r.m02+o.Y*r.m12+o.Z*r.m22,g=(e*r.m00+t*r.m10+n*r.m20)*(p/u),_=(e*r.m01+t*r.m11+n*r.m21)*(m/d),v=(e*r.m02+t*r.m12+n*r.m22)*(h/f),y=g*i.m00+_*i.m10+v*i.m20,b=g*i.m01+_*i.m11+v*i.m21,x=g*i.m02+_*i.m12+v*i.m22,S=ue(y*a.m00+b*a.m10+x*a.m20),C=ue(y*a.m01+b*a.m11+x*a.m21),w=ue(y*a.m02+b*a.m12+x*a.m22);return[S*255,C*255,w*255]},fe=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=he(t,n,r),[c,l,u]=pe(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function pe(e,t,n){let{Xn:r,Yn:i,Zn:a,kE:o,kK:s}=j,c=e/r,l=t/i,u=n/a,d=c>o?c**(1/3):(s*c+16)/116,f=l>o?l**(1/3):(s*l+16)/116,p=u>o?u**(1/3):(s*u+16)/116;return[116*f-16,500*(d-f),200*(f-p)]}function me(e){let t=Math.sign(e);return e=Math.abs(e),(e<=.04045?e/12.92:((e+.055)/1.055)**2.4)*t}var he=(e,t,n)=>{e=me(e/255),t=me(t/255),n=me(n/255);let{MtxRGB2XYZ:r,MtxAdaptMa:i,MtxAdaptMaI:a,Xn:o,Yn:s,Zn:c,As:l,Bs:u,Cs:d}=j,f=e*r.m00+t*r.m10+n*r.m20,p=e*r.m01+t*r.m11+n*r.m21,m=e*r.m02+t*r.m12+n*r.m22,h=o*i.m00+s*i.m10+c*i.m20,g=o*i.m01+s*i.m11+c*i.m21,_=o*i.m02+s*i.m12+c*i.m22,v=f*i.m00+p*i.m10+m*i.m20,y=f*i.m01+p*i.m11+m*i.m21,b=f*i.m02+p*i.m12+m*i.m22;return v*=h/l,y*=g/u,b*=_/d,f=v*a.m00+y*a.m10+b*a.m20,p=v*a.m01+y*a.m11+b*a.m21,m=v*a.m02+y*a.m12+b*a.m22,[f,p,m]};O.prototype.lab=function(){return fe(this._rgb)},Object.assign(k,{lab:(...e)=>new O(...e,`lab`),getLabWhitePoint:ce,setLabWhitePoint:M}),D.format.lab=N,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`lab`),h(e)===`array`&&e.length===3)return`lab`}}),O.prototype.darken=function(e=1){let t=this,n=t.lab();return n[0]-=j.Kn*e,new O(n,`lab`).alpha(t.alpha(),!0)},O.prototype.brighten=function(e=1){return this.darken(-e)},O.prototype.darker=O.prototype.darken,O.prototype.brighter=O.prototype.brighten,O.prototype.get=function(e){let[t,n]=e.split(`.`),r=this[t]();if(n){let e=t.indexOf(n)-(t.substr(0,2)===`ok`?2:0);if(e>-1)return r[e];throw Error(`unknown channel ${n} in mode ${t}`)}else return r};var{pow:ge}=Math,_e=1e-7,ve=20;O.prototype.luminance=function(e,t=`rgb`){if(e!==void 0&&h(e)===`number`){if(e===0)return new O([0,0,0,this._rgb[3]],`rgb`);if(e===1)return new O([255,255,255,this._rgb[3]],`rgb`);let n=this.luminance(),r=ve,i=(n,a)=>{let o=n.interpolate(a,.5,t),s=o.luminance();return Math.abs(e-s)<_e||!r--?o:s>e?i(n,o):i(o,a)};return new O([...(n>e?i(new O([0,0,0]),this):i(this,new O([255,255,255]))).rgb(),this._rgb[3]])}return ye(...this._rgb.slice(0,3))};var ye=(e,t,n)=>(e=be(e),t=be(t),n=be(n),.2126*e+.7152*t+.0722*n),be=e=>(e/=255,e<=.03928?e/12.92:ge((e+.055)/1.055,2.4)),P={},F=(e,t,n=.5,...r)=>{let i=r[0]||`lrgb`;if(!P[i]&&!r.length&&(i=Object.keys(P)[0]),!P[i])throw Error(`interpolation mode ${i} is not defined`);return h(e)!==`object`&&(e=new O(e)),h(t)!==`object`&&(t=new O(t)),P[i](e,t,n).alpha(e.alpha()+n*(t.alpha()-e.alpha()))};O.prototype.mix=O.prototype.interpolate=function(e,t=.5,...n){return F(this,e,t,...n)},O.prototype.premultiply=function(e=!1){let t=this._rgb,n=t[3];return e?(this._rgb=[t[0]*n,t[1]*n,t[2]*n,n],this):new O([t[0]*n,t[1]*n,t[2]*n,n],`rgb`)};var{sin:xe,cos:Se}=Math,Ce=(...e)=>{let[t,n,r]=g(e,`lch`);return isNaN(r)&&(r=0),r*=T,[t,Se(r)*n,xe(r)*n]},we=(...e)=>{e=g(e,`lch`);let[t,n,r]=e,[i,a,o]=Ce(t,n,r),[s,c,l]=N(i,a,o);return[s,c,l,e.length>3?e[3]:1]},Te=(...e)=>we(...E(g(e,`hcl`))),{sqrt:Ee,atan2:De,round:Oe}=Math,ke=(...e)=>{let[t,n,r]=g(e,`lab`),i=Ee(n*n+r*r),a=(De(r,n)*ee+360)%360;return Oe(i*1e4)===0&&(a=NaN),[t,i,a]},Ae=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=fe(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};O.prototype.lch=function(){return Ae(this._rgb)},O.prototype.hcl=function(){return E(Ae(this._rgb))},Object.assign(k,{lch:(...e)=>new O(...e,`lch`),hcl:(...e)=>new O(...e,`hcl`)}),D.format.lch=we,D.format.hcl=Te,[`lch`,`hcl`].forEach(e=>D.autodetect.push({p:2,test:(...t)=>{if(t=g(t,e),h(t)===`array`&&t.length===3)return e}})),O.prototype.saturate=function(e=1){let t=this,n=t.lch();return n[1]+=j.Kn*e,n[1]<0&&(n[1]=0),new O(n,`lch`).alpha(t.alpha(),!0)},O.prototype.desaturate=function(e=1){return this.saturate(-e)},O.prototype.set=function(e,t,n=!1){let[r,i]=e.split(`.`),a=this[r]();if(i){let e=r.indexOf(i)-(r.substr(0,2)===`ok`?2:0);if(e>-1){if(h(t)==`string`)switch(t.charAt(0)){case`+`:a[e]+=+t;break;case`-`:a[e]+=+t;break;case`*`:a[e]*=+t.substr(1);break;case`/`:a[e]/=+t.substr(1);break;default:a[e]=+t}else if(h(t)===`number`)a[e]=t;else throw Error(`unsupported value for Color.set`);let i=new O(a,r);return n?(this._rgb=i._rgb,this):i}throw Error(`unknown channel ${i} in mode ${r}`)}else return a},O.prototype.tint=function(e=.5,...t){return F(this,`white`,e,...t)},O.prototype.shade=function(e=.5,...t){return F(this,`black`,e,...t)},P.rgb=(e,t,n)=>{let r=e._rgb,i=t._rgb;return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`rgb`)};var{sqrt:je,pow:Me}=Math;P.lrgb=(e,t,n)=>{let[r,i,a]=e._rgb,[o,s,c]=t._rgb;return new O(je(Me(r,2)*(1-n)+Me(o,2)*n),je(Me(i,2)*(1-n)+Me(s,2)*n),je(Me(a,2)*(1-n)+Me(c,2)*n),`rgb`)},P.lab=(e,t,n)=>{let r=e.lab(),i=t.lab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`lab`)};var Ne=(e,t,n,r)=>{let i,a;r===`hsl`?(i=e.hsl(),a=t.hsl()):r===`hsv`?(i=e.hsv(),a=t.hsv()):r===`hcg`?(i=e.hcg(),a=t.hcg()):r===`hsi`?(i=e.hsi(),a=t.hsi()):r===`lch`||r===`hcl`?(r=`hcl`,i=e.hcl(),a=t.hcl()):r===`oklch`&&(i=e.oklch().reverse(),a=t.oklch().reverse());let o,s,c,l,u,d;(r.substr(0,1)===`h`||r===`oklch`)&&([o,c,u]=i,[s,l,d]=a);let f,p,m,h;return!isNaN(o)&&!isNaN(s)?(h=s>o&&s-o>180?s-(o+360):s180?s+360-o:s-o,p=o+n*h):isNaN(o)?isNaN(s)?p=NaN:(p=s,(u==1||u==0)&&r!=`hsv`&&(f=l)):(p=o,(d==1||d==0)&&r!=`hsv`&&(f=c)),f===void 0&&(f=c+n*(l-c)),m=u+n*(d-u),r===`oklch`?new O([m,f,p],r):new O([p,f,m],r)},Pe=(e,t,n)=>Ne(e,t,n,`lch`);P.lch=Pe,P.hcl=Pe;var Fe=e=>{if(h(e)==`number`&&e>=0&&e<=16777215)return[e>>16,e>>8&255,e&255,1];throw Error(`unknown num color: `+e)},Ie=(...e)=>{let[t,n,r]=g(e,`rgb`);return(t<<16)+(n<<8)+r};O.prototype.num=function(){return Ie(this._rgb)},Object.assign(k,{num:(...e)=>new O(...e,`num`)}),D.format.num=Fe,D.autodetect.push({p:5,test:(...e)=>{if(e.length===1&&h(e[0])===`number`&&e[0]>=0&&e[0]<=16777215)return`num`}}),P.num=(e,t,n)=>{let r=e.num();return new O(r+n*(t.num()-r),`num`)};var{floor:Le}=Math,Re=(...e)=>{e=g(e,`hcg`);let[t,n,r]=e,i,a,o;r*=255;let s=n*255;if(n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Le(t),c=t-e,l=r*(1-n),u=l+s*(1-c),d=l+s*c,f=l+s;switch(e){case 0:[i,a,o]=[f,d,l];break;case 1:[i,a,o]=[u,f,l];break;case 2:[i,a,o]=[l,f,d];break;case 3:[i,a,o]=[l,u,f];break;case 4:[i,a,o]=[d,l,f];break;case 5:[i,a,o]=[f,l,u];break}}return[i,a,o,e.length>3?e[3]:1]},ze=(...e)=>{let[t,n,r]=g(e,`rgb`),i=y(t,n,r),a=b(t,n,r),o=a-i,s=o*100/255,c=i/(255-o)*100,l;return o===0?l=NaN:(t===a&&(l=(n-r)/o),n===a&&(l=2+(r-t)/o),r===a&&(l=4+(t-n)/o),l*=60,l<0&&(l+=360)),[l,s,c]};O.prototype.hcg=function(){return ze(this._rgb)},k.hcg=(...e)=>new O(...e,`hcg`),D.format.hcg=Re,D.autodetect.push({p:1,test:(...e)=>{if(e=g(e,`hcg`),h(e)===`array`&&e.length===3)return`hcg`}}),P.hcg=(e,t,n)=>Ne(e,t,n,`hcg`);var{cos:Be}=Math,Ve=(...e)=>{e=g(e,`hsi`);let[t,n,r]=e,i,a,o;return isNaN(t)&&(t=0),isNaN(n)&&(n=0),t>360&&(t-=360),t<0&&(t+=360),t/=360,t<1/3?(o=(1-n)/3,i=(1+n*Be(C*t)/Be(w-C*t))/3,a=1-(o+i)):t<2/3?(t-=1/3,i=(1-n)/3,a=(1+n*Be(C*t)/Be(w-C*t))/3,o=1-(i+a)):(t-=2/3,a=(1-n)/3,o=(1+n*Be(C*t)/Be(w-C*t))/3,i=1-(a+o)),i=f(r*i*3),a=f(r*a*3),o=f(r*o*3),[i*255,a*255,o*255,e.length>3?e[3]:1]},{min:He,sqrt:Ue,acos:We}=Math,Ge=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i,a=He(t,n,r),o=(t+n+r)/3,s=o>0?1-a/o:0;return s===0?i=NaN:(i=(t-n+(t-r))/2,i/=Ue((t-n)*(t-n)+(t-r)*(n-r)),i=We(i),r>n&&(i=C-i),i/=C),[i*360,s,o]};O.prototype.hsi=function(){return Ge(this._rgb)},k.hsi=(...e)=>new O(...e,`hsi`),D.format.hsi=Ve,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsi`),h(e)===`array`&&e.length===3)return`hsi`}}),P.hsi=(e,t,n)=>Ne(e,t,n,`hsi`);var Ke=(...e)=>{e=g(e,`hsl`);let[t,n,r]=e,i,a,o;if(n===0)i=a=o=r*255;else{let e=[0,0,0],s=[0,0,0],c=r<.5?r*(1+n):r+n-r*n,l=2*r-c,u=t/360;e[0]=u+1/3,e[1]=u,e[2]=u-1/3;for(let t=0;t<3;t++)e[t]<0&&(e[t]+=1),e[t]>1&&--e[t],6*e[t]<1?s[t]=l+(c-l)*6*e[t]:2*e[t]<1?s[t]=c:3*e[t]<2?s[t]=l+(c-l)*(2/3-e[t])*6:s[t]=l;[i,a,o]=[s[0]*255,s[1]*255,s[2]*255]}return e.length>3?[i,a,o,e[3]]:[i,a,o,1]},qe=(...e)=>{e=g(e,`rgba`);let[t,n,r]=e;t/=255,n/=255,r/=255;let i=y(t,n,r),a=b(t,n,r),o=(a+i)/2,s,c;return a===i?(s=0,c=NaN):s=o<.5?(a-i)/(a+i):(a-i)/(2-a-i),t==a?c=(n-r)/(a-i):n==a?c=2+(r-t)/(a-i):r==a&&(c=4+(t-n)/(a-i)),c*=60,c<0&&(c+=360),e.length>3&&e[3]!==void 0?[c,s,o,e[3]]:[c,s,o]};O.prototype.hsl=function(){return qe(this._rgb)},k.hsl=(...e)=>new O(...e,`hsl`),D.format.hsl=Ke,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsl`),h(e)===`array`&&e.length===3)return`hsl`}}),P.hsl=(e,t,n)=>Ne(e,t,n,`hsl`);var{floor:Je}=Math,Ye=(...e)=>{e=g(e,`hsv`);let[t,n,r]=e,i,a,o;if(r*=255,n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Je(t),s=t-e,c=r*(1-n),l=r*(1-n*s),u=r*(1-n*(1-s));switch(e){case 0:[i,a,o]=[r,u,c];break;case 1:[i,a,o]=[l,r,c];break;case 2:[i,a,o]=[c,r,u];break;case 3:[i,a,o]=[c,l,r];break;case 4:[i,a,o]=[u,c,r];break;case 5:[i,a,o]=[r,c,l];break}}return[i,a,o,e.length>3?e[3]:1]},{min:Xe,max:Ze}=Math,Qe=(...e)=>{e=g(e,`rgb`);let[t,n,r]=e,i=Xe(t,n,r),a=Ze(t,n,r),o=a-i,s,c,l;return l=a/255,a===0?(s=NaN,c=0):(c=o/a,t===a&&(s=(n-r)/o),n===a&&(s=2+(r-t)/o),r===a&&(s=4+(t-n)/o),s*=60,s<0&&(s+=360)),[s,c,l]};O.prototype.hsv=function(){return Qe(this._rgb)},k.hsv=(...e)=>new O(...e,`hsv`),D.format.hsv=Ye,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsv`),h(e)===`array`&&e.length===3)return`hsv`}}),P.hsv=(e,t,n)=>Ne(e,t,n,`hsv`);function $e(e,t){let n=e.length;Array.isArray(e[0])||(e=[e]),Array.isArray(t[0])||(t=t.map(e=>[e]));let r=t[0].length,i=t[0].map((e,n)=>t.map(e=>e[n])),a=e.map(e=>i.map(t=>Array.isArray(e)?e.reduce((e,n,r)=>e+n*(t[r]||0),0):t.reduce((t,n)=>t+n*e,0)));return n===1&&(a=a[0]),r===1?a.map(e=>e[0]):a}var et=(...e)=>{e=g(e,`lab`);let[t,n,r,...i]=e,[a,o,s]=tt([t,n,r]),[c,l,u]=de(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function tt(e){return $e([[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],$e([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],e).map(e=>e**3))}var nt=(...e)=>{let[t,n,r,...i]=g(e,`rgb`);return[...rt(he(t,n,r)),...i.length>0&&i[0]<1?[i[0]]:[]]};function rt(e){return $e([[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],$e([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],e).map(e=>Math.cbrt(e)))}O.prototype.oklab=function(){return nt(this._rgb)},Object.assign(k,{oklab:(...e)=>new O(...e,`oklab`)}),D.format.oklab=et,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklab`),h(e)===`array`&&e.length===3)return`oklab`}}),P.oklab=(e,t,n)=>{let r=e.oklab(),i=t.oklab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`oklab`)},P.oklch=(e,t,n)=>Ne(e,t,n,`oklch`);var{pow:it,sqrt:at,PI:ot,cos:st,sin:ct,atan2:lt}=Math,ut=(e,t=`lrgb`,n=null)=>{let r=e.length;n||=Array.from(Array(r)).map(()=>1);let i=r/n.reduce(function(e,t){return e+t});if(n.forEach((e,t)=>{n[t]*=i}),e=e.map(e=>new O(e)),t===`lrgb`)return dt(e,n);let a=e.shift(),o=a.get(t),s=[],c=0,l=0;for(let e=0;e{let i=e.get(t);u+=e.alpha()*n[r+1];for(let e=0;e=360;)t-=360;o[e]=t}else o[e]=o[e]/s[e];return u/=r,new O(o,t).alpha(u>.99999?1:u,!0)},dt=(e,t)=>{let n=e.length,r=[0,0,0,0];for(let i=0;i.9999999&&(r[3]=1),new O(p(r))},{pow:ft}=Math;function pt(e){let t=`rgb`,n=k(`#ccc`),r=0,i=[0,1],a=[0,1],o=[],s=[0,0],c=!1,l=[],u=!1,d=0,p=1,m=!1,g={},_=!0,v=1,y=function(e){if(e||=[`#fff`,`#000`],e&&h(e)===`string`&&k.brewer&&k.brewer[e.toLowerCase()]&&(e=k.brewer[e.toLowerCase()]),h(e)===`array`){e.length===1&&(e=[e[0],e[0]]),e=e.slice(0);for(let t=0;t=c[n];)n++;return n-1}return 0},x=e=>e,S=e=>e,C=function(e,r){let i,a;if(r??=!1,isNaN(e)||e===null)return n;a=r?e:c&&c.length>2?b(e)/(c.length-2):p===d?1:(e-d)/(p-d),a=S(a),r||(a=x(a)),v!==1&&(a=ft(a,v)),a=s[0]+a*(1-s[0]-s[1]),a=f(a,0,1);let u=Math.floor(a*1e4);if(_&&g[u])i=g[u];else{if(h(l)===`array`)for(let e=0;e=n&&e===o.length-1){i=l[e];break}if(a>n&&ag={};y(e);let T=function(e){let t=k(C(e));return u&&t[u]?t[u]():t};return T.classes=function(e){if(e!=null){if(h(e)===`array`)c=e,i=[e[0],e[e.length-1]];else{let t=k.analyze(i);c=e===0?[t.min,t.max]:k.limits(t,`e`,e)}return T}return c},T.domain=function(e){if(!arguments.length)return a;a=e.slice(0),d=e[0],p=e[e.length-1],o=[];let t=l.length;if(e.length===t&&d!==p)for(let t of Array.from(e))o.push((t-d)/(p-d));else{for(let e=0;e2){let t=e.map((t,n)=>n/(e.length-1)),n=e.map(e=>(e-d)/(p-d));n.every((e,n)=>t[n]===e)||(S=e=>{if(e<=0||e>=1)return e;let r=0;for(;e>=n[r+1];)r++;let i=(e-n[r])/(n[r+1]-n[r]);return t[r]+i*(t[r+1]-t[r])})}}return i=[d,p],T},T.mode=function(e){return arguments.length?(t=e,w(),T):t},T.range=function(e,t){return y(e,t),T},T.out=function(e){return u=e,T},T.spread=function(e){return arguments.length?(r=e,T):r},T.correctLightness=function(e){return e??=!0,m=e,w(),x=m?function(e){let t=C(0,!0).lab()[0],n=C(1,!0).lab()[0],r=t>n,i=C(e,!0).lab()[0],a=t+(n-t)*e,o=i-a,s=0,c=1,l=20;for(;Math.abs(o)>.01&&l-- >0;)(function(){return r&&(o*=-1),o<0?(s=e,e+=(c-e)*.5):(c=e,e+=(s-e)*.5),i=C(e,!0).lab()[0],o=i-a})();return e}:e=>e,T},T.padding=function(e){return e==null?s:(h(e)===`number`&&(e=[e,e]),s=e,T)},T.colors=function(t,n){arguments.length<2&&(n=`hex`);let r=[];if(arguments.length===0)r=l.slice(0);else if(t===1)r=[T(.5)];else if(t>1){let e=i[0],n=i[1]-e;r=mt(0,t,!1).map(r=>T(e+r/(t-1)*n))}else{e=[];let t=[];if(c&&c.length>2)for(let e=1,n=c.length,r=1<=n;r?en;r?e++:e--)t.push((c[e-1]+c[e])*.5);else t=i;r=t.map(e=>T(e))}return k[n]&&(r=r.map(e=>e[n]())),r},T.cache=function(e){return e==null?_:(_=e,T)},T.gamma=function(e){return e==null?v:(v=e,T)},T.nodata=function(e){return e==null?n:(n=k(e),T)},T}function mt(e,t,n){let r=[],i=ea;i?t++:t--)r.push(t);return r}var ht=function(e){let t=[1,1];for(let n=1;nnew O(e)),e.length===2)[n,r]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>n[t]+e*(r[t]-n[t])),`lab`)};else if(e.length===3)[n,r,i]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*n[t]+2*(1-e)*e*r[t]+e*e*i[t]),`lab`)};else if(e.length===4){let a;[n,r,i,a]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*(1-e)*n[t]+3*(1-e)*(1-e)*e*r[t]+3*(1-e)*e*e*i[t]+e*e*e*a[t]),`lab`)}}else if(e.length>=5){let n,r,i;n=e.map(e=>e.lab()),i=e.length-1,r=ht(i),t=function(e){let t=1-e;return new O([0,1,2].map(a=>n.reduce((n,o,s)=>n+r[s]*t**(i-s)*e**s*o[a],0)),`lab`)}}else throw RangeError(`No point in running bezier with only one color.`);return t},_t=e=>{let t=gt(e);return t.scale=()=>pt(t),t},{round:vt}=Math;O.prototype.rgb=function(e=!0){return e===!1?this._rgb.slice(0,3):this._rgb.slice(0,3).map(vt)},O.prototype.rgba=function(e=!0){return this._rgb.slice(0,4).map((t,n)=>n<3?e===!1?t:vt(t):t)},Object.assign(k,{rgb:(...e)=>new O(...e,`rgb`)}),D.format.rgb=(...e)=>{let t=g(e,`rgba`);return t[3]===void 0&&(t[3]=1),t},D.autodetect.push({p:3,test:(...e)=>{if(e=g(e,`rgba`),h(e)===`array`&&(e.length===3||e.length===4&&h(e[3])==`number`&&e[3]>=0&&e[3]<=1))return`rgb`}});var I=(e,t,n)=>{if(!I[n])throw Error(`unknown blend mode `+n);return I[n](e,t)},L=e=>(t,n)=>{let r=k(n).rgb(),i=k(t).rgb();return k.rgb(e(r,i))},R=e=>(t,n)=>{let r=[];return r[0]=e(t[0],n[0]),r[1]=e(t[1],n[1]),r[2]=e(t[2],n[2]),r};I.normal=L(R(e=>e)),I.multiply=L(R((e,t)=>e*t/255)),I.screen=L(R((e,t)=>255*(1-(1-e/255)*(1-t/255)))),I.overlay=L(R((e,t)=>t<128?2*e*t/255:255*(1-2*(1-e/255)*(1-t/255)))),I.darken=L(R((e,t)=>e>t?t:e)),I.lighten=L(R((e,t)=>e>t?e:t)),I.dodge=L(R((e,t)=>e===255?255:(e=t/255*255/(1-e/255),e>255?255:e))),I.burn=L(R((e,t)=>255*(1-(1-t/255)/(e/255))));var{pow:yt,sin:bt,cos:xt}=Math;function St(e=300,t=-1.5,n=1,r=1,i=[0,1]){let a=0,o;h(i)===`array`?o=i[1]-i[0]:(o=0,i=[i,i]);let s=function(s){let c=C*((e+120)/360+t*s),l=yt(i[0]+o*s,r),u=(a===0?n:n[0]+s*a)*l*(1-l)/2,d=xt(c),f=bt(c),m=l+u*(-.14861*d+1.78277*f),h=l+u*(-.29227*d-.90649*f),g=l+1.97294*d*u;return k(p([m*255,h*255,g*255,1]))};return s.start=function(t){return t==null?e:(e=t,s)},s.rotations=function(e){return e==null?t:(t=e,s)},s.gamma=function(e){return e==null?r:(r=e,s)},s.hue=function(e){return e==null?n:(n=e,h(n)===`array`?(a=n[1]-n[0],a===0&&(n=n[1])):a=0,s)},s.lightness=function(e){return e==null?i:(h(e)===`array`?(i=e,o=e[1]-e[0]):(i=[e,e],o=0),s)},s.scale=()=>k.scale(s),s.hue(n),s}var Ct=`0123456789abcdef`,{floor:wt,random:Tt}=Math,Et=(e=Tt)=>{let t=`#`;for(let n=0;n<6;n++)t+=Ct.charAt(wt(e()*16));return new O(t,`hex`)},{log:Dt,pow:Ot,floor:kt,abs:At}=Math;function jt(e,t=null){let n={min:Number.MAX_VALUE,max:Number.MAX_VALUE*-1,sum:0,values:[],count:0};return h(e)===`object`&&(e=Object.values(e)),e.forEach(e=>{t&&h(e)===`object`&&(e=e[t]),e!=null&&!isNaN(e)&&(n.values.push(e),n.sum+=e,en.max&&(n.max=e),n.count+=1)}),n.domain=[n.min,n.max],n.limits=(e,t)=>Mt(n,e,t),n}function Mt(e,t=`equal`,n=7){h(e)==`array`&&(e=jt(e));let{min:r,max:i}=e,a=e.values.sort((e,t)=>e-t);if(n===1)return[r,i];let o=[];if(t.substr(0,1)===`c`&&(o.push(r),o.push(i)),t.substr(0,1)===`e`){o.push(r);for(let e=1;e 0`);let e=Math.LOG10E*Dt(r),t=Math.LOG10E*Dt(i);o.push(r);for(let r=1;r200&&(l=!1)}let f={};for(let e=0;ee-t),o.push(p[0]);for(let e=1;e{e=new O(e),t=new O(t);let n=e.luminance(),r=t.luminance();return n>r?(n+.05)/(r+.05):(r+.05)/(n+.05)},Pt=.027,Ft=5e-4,It=.1,Lt=1.14,Rt=.022,zt=1.414,Bt=(e,t)=>{e=new O(e),t=new O(t),e.alpha()<1&&(e=F(t,e,e.alpha(),`rgb`));let n=Vt(...e.rgb()),r=Vt(...t.rgb()),i=n>=Rt?n:n+(Rt-n)**+zt,a=r>=Rt?r:r+(Rt-r)**+zt,o=a**.56-i**.57,s=a**.65-i**.62,c=Math.abs(a-i)0?c-Pt:c+Pt)*100};function Vt(e,t,n){return .2126729*(e/255)**2.4+.7151522*(t/255)**2.4+.072175*(n/255)**2.4}var{sqrt:z,pow:B,min:Ht,max:Ut,atan2:Wt,abs:Gt,cos:Kt,sin:qt,exp:Jt,PI:Yt}=Math;function Xt(e,t,n=1,r=1,i=1){var a=function(e){return 360*e/(2*Yt)},o=function(e){return 2*Yt*e/360};e=new O(e),t=new O(t);let[s,c,l]=Array.from(e.lab()),[u,d,f]=Array.from(t.lab()),p=(s+u)/2,m=(z(B(c,2)+B(l,2))+z(B(d,2)+B(f,2)))/2,h=.5*(1-z(B(m,7)/(B(m,7)+B(25,7)))),g=c*(1+h),_=d*(1+h),v=z(B(g,2)+B(l,2)),y=z(B(_,2)+B(f,2)),b=(v+y)/2,x=a(Wt(l,g)),S=a(Wt(f,_)),C=x>=0?x:x+360,w=S>=0?S:S+360,T=Gt(C-w)>180?(C+w+360)/2:(C+w)/2,ee=1-.17*Kt(o(T-30))+.24*Kt(o(2*T))+.32*Kt(o(3*T+6))-.2*Kt(o(4*T-63)),E=w-C;E=Gt(E)<=180?E:w<=C?E+360:E-360,E=2*z(v*y)*qt(o(E)/2);let D=u-s,te=y-v,k=1+.015*B(p-50,2)/z(20+B(p-50,2)),A=1+.045*b,ne=1+.015*b*ee,re=30*Jt(-B((T-275)/25,2)),ie=-(2*z(B(b,7)/(B(b,7)+B(25,7))))*qt(2*o(re));return Ut(0,Ht(100,z(B(D/(n*k),2)+B(te/(r*A),2)+B(E/(i*ne),2)+ie*(te/(r*A))*(E/(i*ne)))))}function Zt(e,t,n=`lab`){e=new O(e),t=new O(t);let r=e.get(n),i=t.get(n),a=0;for(let e in r){let t=(r[e]||0)-(i[e]||0);a+=t*t}return Math.sqrt(a)}var Qt=(...e)=>{try{return new O(...e),!0}catch{return!1}},$t={cool(){return pt([k.hsl(180,1,.9),k.hsl(250,.7,.4)])},hot(){return pt([`#000`,`#f00`,`#ff0`,`#fff`],[0,.25,.75,1]).mode(`rgb`)}},en={OrRd:[`#fff7ec`,`#fee8c8`,`#fdd49e`,`#fdbb84`,`#fc8d59`,`#ef6548`,`#d7301f`,`#b30000`,`#7f0000`],PuBu:[`#fff7fb`,`#ece7f2`,`#d0d1e6`,`#a6bddb`,`#74a9cf`,`#3690c0`,`#0570b0`,`#045a8d`,`#023858`],BuPu:[`#f7fcfd`,`#e0ecf4`,`#bfd3e6`,`#9ebcda`,`#8c96c6`,`#8c6bb1`,`#88419d`,`#810f7c`,`#4d004b`],Oranges:[`#fff5eb`,`#fee6ce`,`#fdd0a2`,`#fdae6b`,`#fd8d3c`,`#f16913`,`#d94801`,`#a63603`,`#7f2704`],BuGn:[`#f7fcfd`,`#e5f5f9`,`#ccece6`,`#99d8c9`,`#66c2a4`,`#41ae76`,`#238b45`,`#006d2c`,`#00441b`],YlOrBr:[`#ffffe5`,`#fff7bc`,`#fee391`,`#fec44f`,`#fe9929`,`#ec7014`,`#cc4c02`,`#993404`,`#662506`],YlGn:[`#ffffe5`,`#f7fcb9`,`#d9f0a3`,`#addd8e`,`#78c679`,`#41ab5d`,`#238443`,`#006837`,`#004529`],Reds:[`#fff5f0`,`#fee0d2`,`#fcbba1`,`#fc9272`,`#fb6a4a`,`#ef3b2c`,`#cb181d`,`#a50f15`,`#67000d`],RdPu:[`#fff7f3`,`#fde0dd`,`#fcc5c0`,`#fa9fb5`,`#f768a1`,`#dd3497`,`#ae017e`,`#7a0177`,`#49006a`],Greens:[`#f7fcf5`,`#e5f5e0`,`#c7e9c0`,`#a1d99b`,`#74c476`,`#41ab5d`,`#238b45`,`#006d2c`,`#00441b`],YlGnBu:[`#ffffd9`,`#edf8b1`,`#c7e9b4`,`#7fcdbb`,`#41b6c4`,`#1d91c0`,`#225ea8`,`#253494`,`#081d58`],Purples:[`#fcfbfd`,`#efedf5`,`#dadaeb`,`#bcbddc`,`#9e9ac8`,`#807dba`,`#6a51a3`,`#54278f`,`#3f007d`],GnBu:[`#f7fcf0`,`#e0f3db`,`#ccebc5`,`#a8ddb5`,`#7bccc4`,`#4eb3d3`,`#2b8cbe`,`#0868ac`,`#084081`],Greys:[`#ffffff`,`#f0f0f0`,`#d9d9d9`,`#bdbdbd`,`#969696`,`#737373`,`#525252`,`#252525`,`#000000`],YlOrRd:[`#ffffcc`,`#ffeda0`,`#fed976`,`#feb24c`,`#fd8d3c`,`#fc4e2a`,`#e31a1c`,`#bd0026`,`#800026`],PuRd:[`#f7f4f9`,`#e7e1ef`,`#d4b9da`,`#c994c7`,`#df65b0`,`#e7298a`,`#ce1256`,`#980043`,`#67001f`],Blues:[`#f7fbff`,`#deebf7`,`#c6dbef`,`#9ecae1`,`#6baed6`,`#4292c6`,`#2171b5`,`#08519c`,`#08306b`],PuBuGn:[`#fff7fb`,`#ece2f0`,`#d0d1e6`,`#a6bddb`,`#67a9cf`,`#3690c0`,`#02818a`,`#016c59`,`#014636`],Viridis:[`#440154`,`#482777`,`#3f4a8a`,`#31678e`,`#26838f`,`#1f9d8a`,`#6cce5a`,`#b6de2b`,`#fee825`],Spectral:[`#9e0142`,`#d53e4f`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#e6f598`,`#abdda4`,`#66c2a5`,`#3288bd`,`#5e4fa2`],RdYlGn:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#d9ef8b`,`#a6d96a`,`#66bd63`,`#1a9850`,`#006837`],RdBu:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#f7f7f7`,`#d1e5f0`,`#92c5de`,`#4393c3`,`#2166ac`,`#053061`],PiYG:[`#8e0152`,`#c51b7d`,`#de77ae`,`#f1b6da`,`#fde0ef`,`#f7f7f7`,`#e6f5d0`,`#b8e186`,`#7fbc41`,`#4d9221`,`#276419`],PRGn:[`#40004b`,`#762a83`,`#9970ab`,`#c2a5cf`,`#e7d4e8`,`#f7f7f7`,`#d9f0d3`,`#a6dba0`,`#5aae61`,`#1b7837`,`#00441b`],RdYlBu:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee090`,`#ffffbf`,`#e0f3f8`,`#abd9e9`,`#74add1`,`#4575b4`,`#313695`],BrBG:[`#543005`,`#8c510a`,`#bf812d`,`#dfc27d`,`#f6e8c3`,`#f5f5f5`,`#c7eae5`,`#80cdc1`,`#35978f`,`#01665e`,`#003c30`],RdGy:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#ffffff`,`#e0e0e0`,`#bababa`,`#878787`,`#4d4d4d`,`#1a1a1a`],PuOr:[`#7f3b08`,`#b35806`,`#e08214`,`#fdb863`,`#fee0b6`,`#f7f7f7`,`#d8daeb`,`#b2abd2`,`#8073ac`,`#542788`,`#2d004b`],Set2:[`#66c2a5`,`#fc8d62`,`#8da0cb`,`#e78ac3`,`#a6d854`,`#ffd92f`,`#e5c494`,`#b3b3b3`],Accent:[`#7fc97f`,`#beaed4`,`#fdc086`,`#ffff99`,`#386cb0`,`#f0027f`,`#bf5b17`,`#666666`],Set1:[`#e41a1c`,`#377eb8`,`#4daf4a`,`#984ea3`,`#ff7f00`,`#ffff33`,`#a65628`,`#f781bf`,`#999999`],Set3:[`#8dd3c7`,`#ffffb3`,`#bebada`,`#fb8072`,`#80b1d3`,`#fdb462`,`#b3de69`,`#fccde5`,`#d9d9d9`,`#bc80bd`,`#ccebc5`,`#ffed6f`],Dark2:[`#1b9e77`,`#d95f02`,`#7570b3`,`#e7298a`,`#66a61e`,`#e6ab02`,`#a6761d`,`#666666`],Paired:[`#a6cee3`,`#1f78b4`,`#b2df8a`,`#33a02c`,`#fb9a99`,`#e31a1c`,`#fdbf6f`,`#ff7f00`,`#cab2d6`,`#6a3d9a`,`#ffff99`,`#b15928`],Pastel2:[`#b3e2cd`,`#fdcdac`,`#cbd5e8`,`#f4cae4`,`#e6f5c9`,`#fff2ae`,`#f1e2cc`,`#cccccc`],Pastel1:[`#fbb4ae`,`#b3cde3`,`#ccebc5`,`#decbe4`,`#fed9a6`,`#ffffcc`,`#e5d8bd`,`#fddaec`,`#f2f2f2`]},tn=Object.keys(en),nn=new Map(tn.map(e=>[e.toLowerCase(),e])),rn=typeof Proxy==`function`?new Proxy(en,{get(e,t){let n=t.toLowerCase();if(nn.has(n))return e[nn.get(n)]},getOwnPropertyNames(){return Object.getOwnPropertyNames(tn)}}):en,an=(...e)=>{e=g(e,`cmyk`);let[t,n,r,i]=e,a=e.length>4?e[4]:1;return i===1?[0,0,0,a]:[t>=1?0:255*(1-t)*(1-i),n>=1?0:255*(1-n)*(1-i),r>=1?0:255*(1-r)*(1-i),a]},{max:on}=Math,sn=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i=1-on(t,on(n,r)),a=i<1?1/(1-i):0;return[(1-t-i)*a,(1-n-i)*a,(1-r-i)*a,i]};O.prototype.cmyk=function(){return sn(this._rgb)},Object.assign(k,{cmyk:(...e)=>new O(...e,`cmyk`)}),D.format.cmyk=an,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`cmyk`),h(e)===`array`&&e.length===4)return`cmyk`}});var cn=(...e)=>{let t=g(e,`hsla`),n=_(e)||`lsa`;return t[0]=x(t[0]||0)+`deg`,t[1]=x(t[1]*100)+`%`,t[2]=x(t[2]*100)+`%`,n===`hsla`||t.length>3&&t[3]<1?(t[3]=`/ `+(t.length>3?t[3]:1),n=`hsla`):t.length=3,`${n.substr(0,3)}(${t.join(` `)})`},ln=(...e)=>{let t=g(e,`lab`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=x(t[2]),n===`laba`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lab(${t.join(` `)})`},un=(...e)=>{let t=g(e,`lch`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,n===`lcha`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lch(${t.join(` `)})`},dn=(...e)=>{let t=g(e,`lab`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=S(t[2]),t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklab(${t.join(` `)})`},fn=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=nt(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},pn=(...e)=>{let t=g(e,`lch`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklch(${t.join(` `)})`},{round:mn}=Math,hn=(...e)=>{let t=g(e,`rgba`),n=_(e)||`rgb`;if(n.substr(0,3)===`hsl`)return cn(qe(t),n);if(n.substr(0,3)===`lab`){let e=ce();M(`d50`);let r=ln(fe(t),n);return M(e),r}if(n.substr(0,3)===`lch`){let e=ce();M(`d50`);let r=un(Ae(t),n);return M(e),r}return n.substr(0,5)===`oklab`?dn(nt(t)):n.substr(0,5)===`oklch`?pn(fn(t)):(t[0]=mn(t[0]),t[1]=mn(t[1]),t[2]=mn(t[2]),(n===`rgba`||t.length>3&&t[3]<1)&&(t[3]=`/ `+(t.length>3?t[3]:1),n=`rgba`),`${n.substr(0,3)}(${t.slice(0,n===`rgb`?3:4).join(` `)})`)},gn=(...e)=>{e=g(e,`lch`);let[t,n,r,...i]=e,[a,o,s]=Ce(t,n,r),[c,l,u]=et(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},V=`((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)`,H=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)`,_n=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)`,U=`\\s*`,vn=`\\s+`,yn=`\\s*,\\s*`,bn=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)`,xn=`\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?`,Sn=RegExp(`^rgba?\\(`+U+[V,V,V].join(vn)+xn+`\\)$`),Cn=RegExp(`^rgb\\(`+U+[V,V,V].join(yn)+U+`\\)$`),wn=RegExp(`^rgba\\(`+U+[V,V,V,H].join(yn)+U+`\\)$`),Tn=RegExp(`^hsla?\\(`+U+[bn,_n,_n].join(vn)+xn+`\\)$`),En=RegExp(`^hsl?\\(`+U+[bn,_n,_n].join(yn)+U+`\\)$`),Dn=/^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,On=RegExp(`^lab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),kn=RegExp(`^lch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),An=RegExp(`^oklab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),jn=RegExp(`^oklch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),{round:Mn}=Math,Nn=e=>e.map((e,t)=>t<=2?f(Mn(e),0,255):e),W=(e,t=0,n=100,r=!1)=>(typeof e==`string`&&e.endsWith(`%`)&&(e=parseFloat(e.substring(0,e.length-1))/100,e=r?t+(e+1)*.5*(n-t):t+e*(n-t)),+e),G=(e,t)=>e===`none`?t:e,Pn=e=>{if(e=e.toLowerCase().trim(),e===`transparent`)return[0,0,0,0];let t;if(D.format.named)try{return D.format.named(e)}catch{}if((t=e.match(Sn))||(t=e.match(Cn))){let e=t.slice(1,4);for(let t=0;t<3;t++)e[t]=+W(G(e[t],0),0,255);e=Nn(e);let n=t[4]===void 0?1:+W(t[4],0,1);return e[3]=n,e}if(t=e.match(wn)){let e=t.slice(1,5);for(let t=0;t<4;t++)e[t]=+W(e[t],0,255);return e}if((t=e.match(Tn))||(t=e.match(En))){let e=t.slice(1,4);e[0]=+G(e[0].replace(`deg`,``),0),e[1]=W(G(e[1],0),0,100)*.01,e[2]=W(G(e[2],0),0,100)*.01;let n=Nn(Ke(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(Dn)){let e=t.slice(1,4);e[1]*=.01,e[2]*=.01;let n=Ke(e);for(let e=0;e<3;e++)n[e]=Mn(n[e]);return n[3]=+t[4],n}if(t=e.match(On)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,100),e[1]=W(G(e[1],0),-125,125,!0),e[2]=W(G(e[2],0),-125,125,!0);let n=ce();M(`d50`);let r=Nn(N(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(kn)){let e=t.slice(1,4);e[0]=W(e[0],0,100),e[1]=W(G(e[1],0),0,150,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=ce();M(`d50`);let r=Nn(we(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(An)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),-.4,.4,!0),e[2]=W(G(e[2],0),-.4,.4,!0);let n=Nn(et(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(jn)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),0,.4,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=Nn(gn(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}};Pn.test=e=>Sn.test(e)||Tn.test(e)||On.test(e)||kn.test(e)||An.test(e)||jn.test(e)||Cn.test(e)||wn.test(e)||En.test(e)||Dn.test(e)||e===`transparent`,O.prototype.css=function(e){return hn(this._rgb,e)},k.css=(...e)=>new O(...e,`css`),D.format.css=Pn,D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&Pn.test(e))return`css`}}),D.format.gl=(...e)=>{let t=g(e,`rgba`);return t[0]*=255,t[1]*=255,t[2]*=255,t},k.gl=(...e)=>new O(...e,`gl`),O.prototype.gl=function(){let e=this._rgb;return[e[0]/255,e[1]/255,e[2]/255,e[3]]},O.prototype.hex=function(e){return oe(this._rgb,e)},k.hex=(...e)=>new O(...e,`hex`),D.format.hex=ie,D.autodetect.push({p:4,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&[3,4,5,6,7,8,9].indexOf(e.length)>=0)return`hex`}});var{log:Fn}=Math,In=e=>{let t=e/100,n,r,i;return t<66?(n=255,r=t<6?0:-155.25485562709179-.44596950469579133*(r=t-2)+104.49216199393888*Fn(r),i=t<20?0:-254.76935184120902+.8274096064007395*(i=t-10)+115.67994401066147*Fn(i)):(n=351.97690566805693+.114206453784165*(n=t-55)-40.25366309332127*Fn(n),r=325.4494125711974+.07943456536662342*(r=t-50)-28.0852963507957*Fn(r),i=255),[n,r,i,1]},{round:Ln}=Math,Rn=(...e)=>{let t=g(e,`rgb`),n=t[0],r=t[2],i=1e3,a=4e4,o;for(;a-i>.4;){o=(a+i)*.5;let e=In(o);e[2]/e[0]>=r/n?a=o:i=o}return Ln(o)};O.prototype.temp=O.prototype.kelvin=O.prototype.temperature=function(){return Rn(this._rgb)};var zn=(...e)=>new O(...e,`temp`);Object.assign(k,{temp:zn,kelvin:zn,temperature:zn}),D.format.temp=D.format.kelvin=D.format.temperature=In,O.prototype.oklch=function(){return fn(this._rgb)},Object.assign(k,{oklch:(...e)=>new O(...e,`oklch`)}),D.format.oklch=gn,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklch`),h(e)===`array`&&e.length===3)return`oklch`}}),Object.assign(k,{analyze:jt,average:ut,bezier:_t,blend:I,brewer:rn,Color:O,colors:A,contrast:Nt,contrastAPCA:Bt,cubehelix:St,deltaE:Xt,distance:Zt,input:D,interpolate:F,limits:Mt,mix:F,random:Et,scale:pt,scales:$t,valid:Qt});var K=k,q=class e{constructor(){this.hex=`#000000`,this.rgb_r=0,this.rgb_g=0,this.rgb_b=0,this.xyz_x=0,this.xyz_y=0,this.xyz_z=0,this.luv_l=0,this.luv_u=0,this.luv_v=0,this.lch_l=0,this.lch_c=0,this.lch_h=0,this.hsluv_h=0,this.hsluv_s=0,this.hsluv_l=0,this.hpluv_h=0,this.hpluv_p=0,this.hpluv_l=0,this.r0s=0,this.r0i=0,this.r1s=0,this.r1i=0,this.g0s=0,this.g0i=0,this.g1s=0,this.g1i=0,this.b0s=0,this.b0i=0,this.b1s=0,this.b1i=0}static fromLinear(e){return e<=.0031308?12.92*e:1.055*e**(1/2.4)-.055}static toLinear(e){return e>.04045?((e+.055)/1.055)**2.4:e/12.92}static yToL(t){return t<=e.epsilon?t/e.refY*e.kappa:116*(t/e.refY)**(1/3)-16}static lToY(t){return t<=8?e.refY*t/e.kappa:e.refY*((t+16)/116)**3}static rgbChannelToHex(t){let n=Math.round(t*255),r=n%16,i=(n-r)/16|0;return e.hexChars.charAt(i)+e.hexChars.charAt(r)}static hexToRgbChannel(t,n){let r=e.hexChars.indexOf(t.charAt(n)),i=e.hexChars.indexOf(t.charAt(n+1));return(r*16+i)/255}static distanceFromOriginAngle(e,t,n){let r=t/(Math.sin(n)-e*Math.cos(n));return r<0?1/0:r}static distanceFromOrigin(e,t){return Math.abs(t)/Math.sqrt(e**2+1)}static min6(e,t,n,r,i,a){return Math.min(e,Math.min(t,Math.min(n,Math.min(r,Math.min(i,a)))))}rgbToHex(){this.hex=`#`,this.hex+=e.rgbChannelToHex(this.rgb_r),this.hex+=e.rgbChannelToHex(this.rgb_g),this.hex+=e.rgbChannelToHex(this.rgb_b)}hexToRgb(){this.hex=this.hex.toLowerCase(),this.rgb_r=e.hexToRgbChannel(this.hex,1),this.rgb_g=e.hexToRgbChannel(this.hex,3),this.rgb_b=e.hexToRgbChannel(this.hex,5)}xyzToRgb(){this.rgb_r=e.fromLinear(e.m_r0*this.xyz_x+e.m_r1*this.xyz_y+e.m_r2*this.xyz_z),this.rgb_g=e.fromLinear(e.m_g0*this.xyz_x+e.m_g1*this.xyz_y+e.m_g2*this.xyz_z),this.rgb_b=e.fromLinear(e.m_b0*this.xyz_x+e.m_b1*this.xyz_y+e.m_b2*this.xyz_z)}rgbToXyz(){let t=e.toLinear(this.rgb_r),n=e.toLinear(this.rgb_g),r=e.toLinear(this.rgb_b);this.xyz_x=.41239079926595*t+.35758433938387*n+.18048078840183*r,this.xyz_y=.21263900587151*t+.71516867876775*n+.072192315360733*r,this.xyz_z=.019330818715591*t+.11919477979462*n+.95053215224966*r}xyzToLuv(){let t=this.xyz_x+15*this.xyz_y+3*this.xyz_z,n=4*this.xyz_x,r=9*this.xyz_y;t===0?(n=NaN,r=NaN):(n/=t,r/=t),this.luv_l=e.yToL(this.xyz_y),this.luv_l===0?(this.luv_u=0,this.luv_v=0):(this.luv_u=13*this.luv_l*(n-e.refU),this.luv_v=13*this.luv_l*(r-e.refV))}luvToXyz(){if(this.luv_l===0){this.xyz_x=0,this.xyz_y=0,this.xyz_z=0;return}let t=this.luv_u/(13*this.luv_l)+e.refU,n=this.luv_v/(13*this.luv_l)+e.refV;this.xyz_y=e.lToY(this.luv_l),this.xyz_x=0-9*this.xyz_y*t/((t-4)*n-t*n),this.xyz_z=(9*this.xyz_y-15*n*this.xyz_y-n*this.xyz_x)/(3*n)}luvToLch(){if(this.lch_l=this.luv_l,this.lch_c=Math.sqrt(this.luv_u*this.luv_u+this.luv_v*this.luv_v),this.lch_c<1e-8)this.lch_h=0;else{let e=Math.atan2(this.luv_v,this.luv_u);this.lch_h=e*180/Math.PI,this.lch_h<0&&(this.lch_h=360+this.lch_h)}}lchToLuv(){let e=this.lch_h/180*Math.PI;this.luv_l=this.lch_l,this.luv_u=Math.cos(e)*this.lch_c,this.luv_v=Math.sin(e)*this.lch_c}calculateBoundingLines(t){let n=(t+16)**3/1560896,r=n>e.epsilon?n:t/e.kappa,i=r*(284517*e.m_r0-94839*e.m_r2),a=r*(838422*e.m_r2+769860*e.m_r1+731718*e.m_r0),o=r*(632260*e.m_r2-126452*e.m_r1),s=r*(284517*e.m_g0-94839*e.m_g2),c=r*(838422*e.m_g2+769860*e.m_g1+731718*e.m_g0),l=r*(632260*e.m_g2-126452*e.m_g1),u=r*(284517*e.m_b0-94839*e.m_b2),d=r*(838422*e.m_b2+769860*e.m_b1+731718*e.m_b0),f=r*(632260*e.m_b2-126452*e.m_b1);this.r0s=i/o,this.r0i=a*t/o,this.r1s=i/(o+126452),this.r1i=(a-769860)*t/(o+126452),this.g0s=s/l,this.g0i=c*t/l,this.g1s=s/(l+126452),this.g1i=(c-769860)*t/(l+126452),this.b0s=u/f,this.b0i=d*t/f,this.b1s=u/(f+126452),this.b1i=(d-769860)*t/(f+126452)}calcMaxChromaHpluv(){let t=e.distanceFromOrigin(this.r0s,this.r0i),n=e.distanceFromOrigin(this.r1s,this.r1i),r=e.distanceFromOrigin(this.g0s,this.g0i),i=e.distanceFromOrigin(this.g1s,this.g1i),a=e.distanceFromOrigin(this.b0s,this.b0i),o=e.distanceFromOrigin(this.b1s,this.b1i);return e.min6(t,n,r,i,a,o)}calcMaxChromaHsluv(t){let n=t/360*Math.PI*2,r=e.distanceFromOriginAngle(this.r0s,this.r0i,n),i=e.distanceFromOriginAngle(this.r1s,this.r1i,n),a=e.distanceFromOriginAngle(this.g0s,this.g0i,n),o=e.distanceFromOriginAngle(this.g1s,this.g1i,n),s=e.distanceFromOriginAngle(this.b0s,this.b0i,n),c=e.distanceFromOriginAngle(this.b1s,this.b1i,n);return e.min6(r,i,a,o,s,c)}hsluvToLch(){if(this.hsluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hsluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hsluv_l,this.calculateBoundingLines(this.hsluv_l);let e=this.calcMaxChromaHsluv(this.hsluv_h);this.lch_c=e/100*this.hsluv_s}this.lch_h=this.hsluv_h}lchToHsluv(){if(this.lch_l>99.9999999)this.hsluv_s=0,this.hsluv_l=100;else if(this.lch_l<1e-8)this.hsluv_s=0,this.hsluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHsluv(this.lch_h);this.hsluv_s=this.lch_c/e*100,this.hsluv_l=this.lch_l}this.hsluv_h=this.lch_h}hpluvToLch(){if(this.hpluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hpluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hpluv_l,this.calculateBoundingLines(this.hpluv_l);let e=this.calcMaxChromaHpluv();this.lch_c=e/100*this.hpluv_p}this.lch_h=this.hpluv_h}lchToHpluv(){if(this.lch_l>99.9999999)this.hpluv_p=0,this.hpluv_l=100;else if(this.lch_l<1e-8)this.hpluv_p=0,this.hpluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHpluv();this.hpluv_p=this.lch_c/e*100,this.hpluv_l=this.lch_l}this.hpluv_h=this.lch_h}hsluvToRgb(){this.hsluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hpluvToRgb(){this.hpluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hsluvToHex(){this.hsluvToRgb(),this.rgbToHex()}hpluvToHex(){this.hpluvToRgb(),this.rgbToHex()}rgbToHsluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHsluv()}rgbToHpluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHpluv()}hexToHsluv(){this.hexToRgb(),this.rgbToHsluv()}hexToHpluv(){this.hexToRgb(),this.rgbToHpluv()}};q.hexChars=`0123456789abcdef`,q.refY=1,q.refU=.19783000664283,q.refV=.46831999493879,q.kappa=903.2962962,q.epsilon=.0088564516,q.m_r0=3.240969941904521,q.m_r1=-1.537383177570093,q.m_r2=-.498610760293,q.m_g0=-.96924363628087,q.m_g1=1.87596750150772,q.m_g2=.041555057407175,q.m_b0=.055630079696993,q.m_b1=-.20397695888897,q.m_b2=1.056971514242878;var Bn=s(((e,t)=>{function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.exports=n})),Vn=s(((e,t)=>{var n=Bn(),r,i;function a(){for(var e in i=[`toString`,`toLocaleString`,`valueOf`,`hasOwnProperty`,`isPrototypeOf`,`propertyIsEnumerable`,`constructor`],r=!0,{toString:null})r=!1}function o(e,t,o){var c,l=0;for(c in r??a(),e)if(s(t,e,c,o)===!1)break;if(r)for(var u=e.constructor,d=!!u&&e===u.prototype;(c=i[l++])&&!((c!==`constructor`||!d&&n(e,c))&&e[c]!==Object.prototype[c]&&s(t,e,c,o)===!1););}function s(e,t,n,r){return e.call(r,t[n],n,t)}t.exports=o})),Hn=s(((e,t)=>{var n=Vn();function r(e){var t=[];return n(e,function(e,n){typeof e==`function`&&t.push(n)}),t.sort()}t.exports=r})),Un=s(((e,t)=>{function n(e,t,n){var r=e.length;t=t==null?0:t<0?Math.max(r+t,0):Math.min(t,r),n=n==null?r:n<0?Math.max(r+n,0):Math.min(n,r);for(var i=[];t{var n=Un();function r(e,t,r){var i=n(arguments,2);return function(){return e.apply(t,i.concat(n(arguments)))}}t.exports=r})),Gn=s(((e,t)=>{function n(e,t,n){if(e!=null)for(var r=-1,i=e.length;++r{var n=Hn(),r=Wn(),i=Gn(),a=Un();function o(e,t){i(arguments.length>1?a(arguments,1):n(e),function(t){e[t]=r(e[t],e)})}t.exports=o})),J=s(((e,t)=>{var n=Bn(),r=Vn();function i(e,t,i){r(e,function(r,a){if(n(e,a))return t.call(i,e[a],a,e)})}t.exports=i})),qn=s(((e,t)=>{function n(e){return e}t.exports=n})),Jn=s(((e,t)=>{function n(e){return function(t){return t[e]}}t.exports=n})),Yn=s(((e,t)=>{var n=/^\[object (.*)\]$/,r=Object.prototype.toString,i;function a(e){return e===null?`Null`:e===i?`Undefined`:n.exec(r.call(e))[1]}t.exports=a})),Xn=s(((e,t)=>{var n=Yn();function r(e,t){return n(e)===t}t.exports=r})),Zn=s(((e,t)=>{var n=Xn();t.exports=Array.isArray||function(e){return n(e,`Array`)}})),Qn=s(((e,t)=>{var n=J(),r=Zn();function i(e,t){for(var n=-1,r=e.length;++n{var n=qn(),r=Jn(),i=Qn();function a(e,t){if(e==null)return n;switch(typeof e){case`function`:return t===void 0?e:function(n,r,i){return e.call(t,n,r,i)};case`object`:return function(t){return i(t,e)};case`string`:case`number`:return r(e)}}t.exports=a})),$n=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!1;return n(e,function(n,r){if(t(n,r,e))return a=!0,!1}),a}t.exports=i})),er=s(((e,t)=>{var n=$n();function r(e,t){return n(e,function(e){return e===t})}t.exports=r})),tr=s(((e,t)=>{function n(e){return!!e&&typeof e==`object`&&e.constructor===Object}t.exports=n})),nr=s(((e,t)=>{var n=J(),r=tr();function i(e,t){for(var a=0,o=arguments.length,s;++a{var n=J(),r=tr();function i(e,t){for(var r=0,i=arguments.length,o;++r{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!0;return n(e,function(n,r){if(!t(n,r,e))return a=!1,!1}),a}t.exports=i})),ar=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Object`)}t.exports=r})),or=s(((e,t)=>{function n(e,t){return e===t?e!==0||1/e==1/t:e!==e&&t!==t}t.exports=n})),sr=s(((e,t)=>{var n=Bn(),r=ir(),i=ar(),a=or();function o(e){return function(t,r){return n(this,r)&&e(t,this[r])}}function s(e,t){return n(this,t)}function c(e,t,n){return n||=a,!i(e)||!i(t)?n(e,t):r(e,o(n),t)&&r(t,s,e)}t.exports=c})),cr=s(((e,t)=>{var n=Gn(),r=Un(),i=J();function a(e,t){return n(r(arguments,1),function(t){i(t,function(t,n){e[n]??(e[n]=t)})}),e}t.exports=a})),lr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){t(e,n,r)&&(a[n]=e)}),a}t.exports=i})),ur=s(((e,t)=>{var n=$n(),r=Y();function i(e,t,i){t=r(t,i);var a;return n(e,function(e,n,r){if(t(e,n,r))return a=e,!0}),a}t.exports=i})),dr=s(((e,t)=>{var n=J(),r=tr();function i(e,t,a,o){return n(e,function(e,n){var s=a?a+`.`+n:n;o!==0&&r(e)?i(e,t,s,o-1):t[s]=e}),t}function a(e,t){return e==null?{}:(t??=-1,i(e,{},``,t))}t.exports=a})),fr=s(((e,t)=>{function n(e){switch(typeof e){case`string`:case`number`:case`boolean`:return!0}return e==null}t.exports=n})),pr=s(((e,t)=>{fr();function n(e,t){for(var n=t.split(`.`),r=n.pop();t=n.shift();)if(e=e[t],e==null)return;return e[r]}t.exports=n})),mr=s(((e,t)=>{var n=pr(),r;function i(e,t){return n(e,t)!==r}t.exports=i})),hr=s(((e,t)=>{var n=J();t.exports=Object.keys||function(e){var t=[];return n(e,function(e,n){t.push(n)}),t}})),gr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){a[n]=t(e,n,r)}),a}t.exports=i})),_r=s(((e,t)=>{var n=J();function r(e,t){var r=!0;return n(t,function(t,n){if(e[n]!==t)return r=!1}),r}t.exports=r})),vr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return 1/0;if(e.length&&!t)return Math.max.apply(Math,e);t=n(t,r);for(var i,a=-1/0,o,s,c=-1,l=e.length;++ca&&(a=s,i=o);return i}t.exports=r})),yr=s(((e,t)=>{var n=J();function r(e){var t=[];return n(e,function(e,n){t.push(e)}),t}t.exports=r})),br=s(((e,t)=>{var n=vr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),xr=s(((e,t)=>{var n=J();function r(e,t){for(var r=0,a=arguments.length,o;++r{var n=Yn(),r=tr(),i=xr();function a(e){switch(n(e)){case`Object`:return o(e);case`Array`:return l(e);case`RegExp`:return s(e);case`Date`:return c(e);default:return e}}function o(e){return r(e)?i({},e):e}function s(e){var t=``;return t+=e.multiline?`m`:``,t+=e.global?`g`:``,t+=e.ignoreCase?`i`:``,new RegExp(e.source,t)}function c(e){return new Date(+e)}function l(e){return e.slice()}t.exports=a})),Cr=s(((e,t)=>{var n=Sr(),r=J(),i=Yn(),a=tr();function o(e,t){switch(i(e)){case`Object`:return s(e,t);case`Array`:return c(e,t);default:return n(e)}}function s(e,t){if(a(e)){var n={};return r(e,function(e,n){this[n]=o(e,t)},n),n}else if(t)return t(e);else return e}function c(e,t){for(var n=[],r=-1,i=e.length;++r{var n=Bn(),r=Cr(),i=ar();function a(){for(var e=1,t,o,s,c=r(arguments[0]);s=arguments[e++];)for(t in s)n(s,t)&&(o=s[t],i(o)&&i(c[t])?c[t]=a(c[t],o):c[t]=r(o));return c}t.exports=a})),Tr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return-1/0;if(e.length&&!t)return Math.min.apply(Math,e);t=n(t,r);for(var i,a=1/0,o,s,c=-1,l=e.length;++c{var n=Tr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),Dr=s(((e,t)=>{var n=Gn();function r(e,t){return t&&n(t.split(`.`),function(t){e[t]||(e[t]={}),e=e[t]}),e}t.exports=r})),Or=s(((e,t)=>{function n(e,t,n){if(n||=0,e==null)return-1;for(var r=e.length,i=n<0?r+n:n;i{var n=Or();function r(e,t){return n(e,t)!==-1}t.exports=r})),Ar=s(((e,t)=>{var n=Un(),r=kr();function i(e,t){var i=typeof arguments[1]==`string`?n(arguments,1):arguments[1],a={};for(var o in e)e.hasOwnProperty(o)&&!r(i,o)&&(a[o]=e[o]);return a}t.exports=i})),jr=s(((e,t)=>{var n=Un();function r(e,t){for(var r=typeof arguments[1]==`string`?n(arguments,1):arguments[1],i={},a=0,o;o=r[a++];)i[o]=e[o];return i}t.exports=r})),Mr=s(((e,t)=>{var n=gr(),r=Jn();function i(e,t){return n(e,r(t))}t.exports=i})),Nr=s(((e,t)=>{var n=J();function r(e){var t=0;return n(e,function(){t++}),t}t.exports=r})),Pr=s(((e,t)=>{var n=J(),r=Nr();function i(e,t,i,a){var o=arguments.length>2;if(!r(e)&&!o)throw Error(`reduce of empty object with no initial value`);return n(e,function(e,n,r){o?i=t.call(a,i,e,n,r):(i=e,o=!0)}),i}t.exports=i})),Fr=s(((e,t)=>{var n=lr(),r=Y();function i(e,t,i){return t=r(t,i),n(e,function(e,n,r){return!t(e,n,r)},i)}t.exports=i})),Ir=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Function`)}t.exports=r})),Lr=s(((e,t)=>{var n=Ir();function r(e,t){var r=e[t];if(r!==void 0)return n(r)?r.call(e):r}t.exports=r})),Rr=s(((e,t)=>{var n=Dr();function r(e,t,r){var i=/^(.+)\.(.+)$/.exec(t);i?n(e,i[1])[i[2]]=r:e[t]=r}t.exports=r})),zr=s(((e,t)=>{var n=mr();function r(e,t){if(n(e,t)){for(var r=t.split(`.`),i=r.pop();t=r.shift();)e=e[t];return delete e[i]}else return!0}t.exports=r})),Br=s(((e,t)=>{t.exports={bindAll:Kn(),contains:er(),deepFillIn:nr(),deepMatches:Qn(),deepMixIn:rr(),equals:sr(),every:ir(),fillIn:cr(),filter:lr(),find:ur(),flatten:dr(),forIn:Vn(),forOwn:J(),functions:Hn(),get:pr(),has:mr(),hasOwn:Bn(),keys:hr(),map:gr(),matches:_r(),max:br(),merge:wr(),min:Er(),mixIn:xr(),namespace:Dr(),omit:Ar(),pick:jr(),pluck:Mr(),reduce:Pr(),reject:Fr(),result:Lr(),set:Rr(),size:Nr(),some:$n(),unset:zr(),values:yr()}})),Vr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.default=(0,Br().map)({A:{x:.44758,y:.40745},C:{x:.31006,y:.31616},D50:{x:.34567,y:.35851},D65:{x:.31272,y:.32903},D55:{x:.33243,y:.34744},D75:{x:.29903,y:.31488}},function(e){return[100*(e.x/e.y),100,100*(1-e.x-e.y)/e.y]}),t.exports=e.default})),Hr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=Math,r=n.pow,i=n.sign,a=n.abs,o={decode:function(e){return e<=.04045?e/12.92:r((e+.055)/1.055,2.4)},encode:function(e){return e<=.0031308?12.92*e:1.055*r(e,1/2.4)-.055}},s={encode:function(e){return e<.001953125?16*e:r(e,1/1.8)},decode:function(e){return e<16*.001953125?e/16:r(e,1.8)}};function c(e){return{decode:function(t){return i(t)*r(a(t),e)},encode:function(t){return i(t)*r(a(t),1/e)}}}e.default={sRGB:{r:{x:.64,y:.33},g:{x:.3,y:.6},b:{x:.15,y:.06},gamma:o},"Adobe RGB":{r:{x:.64,y:.33},g:{x:.21,y:.71},b:{x:.15,y:.06},gamma:c(2.2)},"Wide Gamut RGB":{r:{x:.7347,y:.2653},g:{x:.1152,y:.8264},b:{x:.1566,y:.0177},gamma:c(563/256)},"ProPhoto RGB":{r:{x:.7347,y:.2653},g:{x:.1596,y:.8404},b:{x:.0366,y:1e-4},gamma:s}},t.exports=e.default})),Ur=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});function t(e){return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function n(e){return e[0][0]*(e[2][2]*e[1][1]-e[2][1]*e[1][2])+e[1][0]*(e[2][1]*e[0][2]-e[2][2]*e[0][1])+e[2][0]*(e[1][2]*e[0][1]-e[1][1]*e[0][2])}function r(e){var t=1/n(e);return[[(e[2][2]*e[1][1]-e[2][1]*e[1][2])*t,(e[2][1]*e[0][2]-e[2][2]*e[0][1])*t,(e[1][2]*e[0][1]-e[1][1]*e[0][2])*t],[(e[2][0]*e[1][2]-e[2][2]*e[1][0])*t,(e[2][2]*e[0][0]-e[2][0]*e[0][2])*t,(e[1][0]*e[0][2]-e[1][2]*e[0][0])*t],[(e[2][1]*e[1][0]-e[2][0]*e[1][1])*t,(e[2][0]*e[0][1]-e[2][1]*e[0][0])*t,(e[1][1]*e[0][0]-e[1][0]*e[0][1])*t]]}function i(e,t){return[e[0][0]*t[0]+e[0][1]*t[1]+e[0][2]*t[2],e[1][0]*t[0]+e[1][1]*t[1]+e[1][2]*t[2],e[2][0]*t[0]+e[2][1]*t[1]+e[2][2]*t[2]]}function a(e,t){return[[e[0][0]*t[0],e[0][1]*t[1],e[0][2]*t[2]],[e[1][0]*t[0],e[1][1]*t[1],e[1][2]*t[2]],[e[2][0]*t[0],e[2][1]*t[1],e[2][2]*t[2]]]}function o(e,t){return[[e[0][0]*t[0][0]+e[0][1]*t[1][0]+e[0][2]*t[2][0],e[0][0]*t[0][1]+e[0][1]*t[1][1]+e[0][2]*t[2][1],e[0][0]*t[0][2]+e[0][1]*t[1][2]+e[0][2]*t[2][2]],[e[1][0]*t[0][0]+e[1][1]*t[1][0]+e[1][2]*t[2][0],e[1][0]*t[0][1]+e[1][1]*t[1][1]+e[1][2]*t[2][1],e[1][0]*t[0][2]+e[1][1]*t[1][2]+e[1][2]*t[2][2]],[e[2][0]*t[0][0]+e[2][1]*t[1][0]+e[2][2]*t[2][0],e[2][0]*t[0][1]+e[2][1]*t[1][1]+e[2][2]*t[2][1],e[2][0]*t[0][2]+e[2][1]*t[1][2]+e[2][2]*t[2][2]]]}e.transpose=t,e.determinant=n,e.inverse=r,e.multiply=i,e.scalar=a,e.product=o})),Wr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.PI;function n(e){for(var n=e*180/t;n<0;)n+=360;for(;n>360;)n-=360;return n}function r(e){for(var n=t*e/180;n<0;)n+=2*t;for(;n>2*t;)n-=2*t;return n}e.fromRadian=n,e.toRadian=r})),Gr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.round;function n(e){return e[0]==`#`&&(e=e.slice(1)),e.length<6&&(e=e.split(``).map(function(e){return e+e}).join(``)),e.match(/../g).map(function(e){return parseInt(e,16)/255})}function r(e){return`#`+e.map(function(e){return e=t(255*e).toString(16),e.length<2&&(e=`0`+e),e}).join(``)}e.fromHex=n,e.toHex=r})),Kr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=o(Ur()),r=a(Vr()),i=a(Hr());function a(e){return e&&e.__esModule?e:{default:e}}function o(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function s(){var e=arguments.length<=0||arguments[0]===void 0?i.default.sRGB:arguments[0],t=arguments.length<=1||arguments[1]===void 0?r.default.D65:arguments[1],a=[e.r,e.g,e.b],o=n.transpose(a.map(function(e){return[e.x/e.y,1,(1-e.x-e.y)/e.y]})),s=e.gamma,c=n.multiply(n.inverse(o),t),l=n.scalar(o,c),u=n.inverse(l);return{fromRgb:function(e){return n.multiply(l,e.map(s.decode))},toRgb:function(e){return n.multiply(u,e).map(s.encode)}}}e.default=s,t.exports=e.default})),qr=s(((e,t)=>{t.exports={illuminant:Vr(),workspace:Hr(),matrix:Ur(),degree:Wr(),rgb:Gr(),xyz:Kr()}})),Jr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.cfs=e.distance=e.lerp=e.corLerp=void 0;var t=Br();function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);ti/2&&(e>t?t+=i:e+=i),((1-n)*e+n*t)%(i||1/0)}function u(e,t,n){var r={};for(var i in e)r[i]=l(e[i],t[i],n,i);return r}function d(e,t){var n=0;for(var r in e)n+=o(e[r]-t[r],2);return s(n)}function f(e){return t.merge.apply(void 0,r(e.split(``).map(function(e){return n({},e,!0)})))}e.corLerp=l,e.lerp=u,e.distance=d,e.cfs=f})),Yr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=Jr();function a(e,t){var a=arguments.length<=2||arguments[2]===void 0?1e-6:arguments[2],o=-a,s=1+a,c=Math,l=c.min,u=c.max,d=n([`000`,`fff`].map(function(n){return t.fromXyz(e.fromRgb(r.rgb.fromHex(n)))}),2),f=d[0],p=d[1];function m(n){var r=e.toRgb(t.toXyz(n));return[r.map(function(e){return e>=o&&e<=s}).reduce(function(e,t){return e&&t},!0),r]}function h(e,t){for(var r=arguments.length<=2||arguments[2]===void 0?.001:arguments[2];(0,i.distance)(e,t)>r;){var a=(0,i.lerp)(e,t,.5);n(m(a),1)[0]?e=a:t=a}return e}function g(e){return(0,i.lerp)(f,p,e)}function _(e){return e.map(function(e){return u(o,l(s,e))})}return{contains:m,limit:h,spine:g,crop:_}}e.default=a,t.exports=e.default})),Xr=s((e=>{var t=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0}),e.toNotation=e.fromNotation=e.toHue=e.fromHue=void 0;var n=Jr(),r=Math.floor,i=[{s:`R`,h:20.14,e:.8,H:0},{s:`Y`,h:90,e:.7,H:100},{s:`G`,h:164.25,e:1,H:200},{s:`B`,h:237.53,e:1.2,H:300},{s:`R`,h:380.14,e:.8,H:400}],a=i.map(function(e){return e.s}).slice(0,-1).join(``);function o(e){e50){var o=[n,t];t=o[0],n=o[1],i=100-i}return i<1?a[t]:a[t]+i.toFixed()+a[n]}e.fromHue=o,e.toHue=s,e.fromNotation=l,e.toNotation=u})),Zr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=s(Xr()),a=Jr(),o=Br();function s(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var c=Math,l=c.pow,u=c.sqrt,d=c.exp,f=c.abs,p=c.sign,m=Math,h=m.sin,g=m.cos,_=m.atan2,v={average:{F:1,c:.69,N_c:1},dim:{F:.9,c:.59,N_c:.9},dark:{F:.8,c:.535,N_c:.8}},y=[[.7328,.4296,-.1624],[-.7036,1.6975,.0061],[.003,.0136,.9834]],b=[[.38971,.68898,-.07868],[-.22981,1.1834,.04641],[0,0,1]],x=y,S=r.matrix.inverse(y),C=r.matrix.product(b,r.matrix.inverse(y)),w=r.matrix.product(y,r.matrix.inverse(b)),T={whitePoint:r.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ee=(0,a.cfs)(`QJMCshH`),E=(0,a.cfs)(`JCh`);function D(){var e=arguments.length<=0||arguments[0]===void 0?{}:arguments[0],t=arguments.length<=1||arguments[1]===void 0?ee:arguments[1];e=(0,o.merge)(T,e);var a=e.whitePoint,s=e.adaptingLuminance,c=e.backgroundLuminance,m=v[e.surroundType],b=m.F,D=m.c,O=m.N_c,te=a[1],k=1/(5*s+1),A=.2*l(k,4)*5*s+.1*l(1-l(k,4),2)*l(5*s,1/3),ne=c/te,re=.725*l(1/ne,.2),ie=re,ae=1.48+u(ne),oe=e.discounting?1:b*(1-1/3.6*d(-(s+42)/92)),j=n(r.matrix.multiply(y,a).map(function(e){return oe*te/e+1-oe}),3),se=j[0],M=j[1],ce=j[2],N=pe(de(le(a)));function le(e){var t=n(r.matrix.multiply(x,e),3),i=t[0],a=t[1],o=t[2];return[se*i,M*a,ce*o]}function ue(e){var t=n(e,3),i=t[0],a=t[1],o=t[2];return r.matrix.multiply(S,[i/se,a/M,o/ce])}function de(e){return r.matrix.multiply(C,e).map(function(e){var t=l(A*f(e)/100,.42);return p(e)*400*t/(27.13+t)+.1})}function fe(e){return r.matrix.multiply(w,e.map(function(e){var t=e-.1;return p(t)*100/A*l(27.13*f(t)/(400-f(t)),1/.42)}))}function pe(e){var t=n(e,3),r=t[0],i=t[1],a=t[2];return(r*2+i+a/20-.305)*re}function me(e){return 4/D*u(e/100)*(N+4)*l(A,.25)}function he(e){return 6.25*l(D*e/((N+4)*l(A,.25)),2)}function ge(e){return e*l(A,.25)}function _e(e,t){return l(e/100,2)*t/l(A,.25)}function ve(e){return e/l(A,.25)}function ye(e,t){return 100*u(e/t)}function be(e,t){var n=t.Q,r=t.J,a=t.M,o=t.C,s=t.s,c=t.h,l=t.H,u={};return e.J&&(u.J=isNaN(r)?he(n):r),e.C&&(isNaN(o)?isNaN(a)?(n=isNaN(n)?me(r):n,u.C=_e(s,n)):u.C=ve(a):u.C=t.C),e.h&&(u.h=isNaN(c)?i.toHue(l):c),e.Q&&(u.Q=isNaN(n)?me(r):n),e.M&&(u.M=isNaN(a)?ge(o):a),e.s&&(isNaN(s)?(n=isNaN(n)?me(r):n,a=isNaN(a)?ge(o):a,u.s=ye(a,n)):u.s=s),e.H&&(u.H=isNaN(l)?i.fromHue(c):l),u}function P(e){var i=de(le(e)),a=n(i,3),o=a[0],s=a[1],c=a[2],d=o-s*12/11+c/11,f=(o+s-2*c)/9,p=_(f,d),m=r.degree.fromRadian(p),h=1/4*(g(p+2)+3.8),v=100*l(pe(i)/N,D*ae);return be(t,{J:v,C:l(5e4/13*O*ie*h*u(d*d+f*f)/(o+s+21/20*c),.9)*u(v/100)*l(1.64-l(.29,ne),.73),h:m})}function F(e){var t=be(E,e),n=t.J,i=t.C,a=t.h,o=r.degree.toRadian(a),s=l(i/(u(n/100)*l(1.64-l(.29,ne),.73)),10/9),c=1/4*(g(o+2)+3.8),d=N*l(n/100,1/D/ae),p=5e4/13*O*ie*c/s,m=d/re+.305,_=m*61/20*460/1403,v=61/20*220/1403,y=21/20*6300/1403-27/1403,b=h(o),x=g(o),S,C;return s===0||isNaN(s)?S=C=0:f(b)>=f(x)?(C=_/(p/b+v*x/b+y),S=C*x/b):(S=_/(p/x+v+y*b/x),C=S*b/x),ue(fe([20/61*m+451/1403*S+288/1403*C,20/61*m-891/1403*S-261/1403*C,20/61*m-220/1403*S-6300/1403*C]))}return{fromXyz:P,toXyz:F,fillOut:be}}e.default=D,t.exports=e.default})),Qr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=qr(),r=Math,i=r.sqrt,a=r.pow,o=r.exp,s=r.log,c=r.cos,l=r.sin,u=r.atan2,d={LCD:{K_L:.77,c_1:.007,c_2:.0053},SCD:{K_L:1.24,c_1:.007,c_2:.0363},UCS:{K_L:1,c_1:.007,c_2:.0228}};function f(){var e=d[arguments.length<=0||arguments[0]===void 0?`UCS`:arguments[0]],t=e.K_L,r=e.c_1,f=e.c_2;function p(e){var t=e.J,i=e.M,a=e.h,o=n.degree.toRadian(a),u=(1+100*r)*t/(1+r*t),d=1/f*s(1+f*i);return{J_p:u,a_p:d*c(o),b_p:d*l(o)}}function m(e){var t=e.J_p,s=e.a_p,c=e.b_p,l=-t/(r*t-100*r-1),d=(o(f*i(a(s,2)+a(c,2)))-1)/f,p=u(c,s);return{J:l,M:d,h:n.degree.fromRadian(p)}}function h(e,n){return i(a((e.J_p-n.J_p)/t,2)+a(e.a_p-n.a_p,2)+a(e.b_p-n.b_p,2))}return{fromCam:p,toCam:m,distance:h}}e.default=f,t.exports=e.default})),$r=s(((e,t)=>{var n=Jr(),r=Yr(),i=Zr(),a=Qr(),o=Xr();t.exports={gamut:r,cfs:n.cfs,lerp:n.lerp,cam:i,ucs:a,hq:o}})),ei=l(qr(),1),ti=l($r(),1);function ni(e){let t=new q;return t.rgb_r=e[0],t.rgb_g=e[1],t.rgb_b=e[2],t.rgbToHsluv(),[t.hsluv_h,t.hsluv_s,t.hsluv_l]}function ri(e){let t=new q;return t.hsluv_h=e[0],t.hsluv_s=e[1],t.hsluv_l=e[2],t.hsluvToRgb(),[t.rgb_r,t.rgb_g,t.rgb_b]}var ii=ti.default.cam({whitePoint:ei.default.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ti.default.cfs(`JCh`)),ai=ei.default.xyz(ei.default.workspace.sRGB,ei.default.illuminant.D65),oi=e=>ai.toRgb(ii.toXyz({J:e[0],C:e[1],h:e[2]})),si=e=>{let t=ii.fromXyz(ai.fromRgb(e));return[t.J,t.C,t.h]},[ci,li]=(()=>{let e={k_l:1,c1:.007,c2:.0228},t=Math.PI,n=64/t/5,r=1/(5*n+1),i=.2*r**4*(5*n)+.1*(1-r**4)**2*(5*n)**(1/3);return[n=>{let[r,a,o]=n,s=a*i**.25,c=(1+100*e.c1)*r/(1+e.c1*r);c/=e.k_l;let l=1/e.c2*Math.log(1+e.c2*s),u=l*Math.cos(t/180*o),d=l*Math.sin(t/180*o);return[c,u,d]},n=>{let[r,a,o]=n,s=Math.sqrt(a*a+o*o),c=(Math.exp(s*e.c2)-1)/e.c2,l=(180/t*Math.atan2(o,a)+360)%360,u=c/i**.25;return[r/(1+e.c1*(100-r)),u,l]}]})(),ui=e=>oi(li(e)),di=e=>ci(si(e)),fi=console;fi.color=(e,t=``)=>{let n=K(e).luminance();fi.log(`%c${e} ${t}`,`background-color: ${e};padding: 5px; border-radius: 5px; color: ${n>.5?`#000`:`#fff`}`)},fi.ramp=(e,t=1)=>{fi.log(`%c `,`font-size: 1px;line-height: 16px;background: ${K.getCSSGradient(e,t)};padding: 0 0 0 200px; border-radius: 2px;`)};var pi=(e,t,n,r,i,a,o=.1)=>{if(e===n||t===r)return!0;let s=(r-t)/(n-e),c=(a+i/s-t+s*e)/(s+1/s),l=a+i/s-c/s;return(i-c)**2+(a-l)**2{let i=(t[0]+n[0])/2,a=e(i);return pi(...t,...n,i,a,r)?null:[i,a]},hi=(e,t,n,r=.1)=>{let i=(n-t)/10,a=[];for(let r=t;rMath.round(e*10**t)/10**t,_i=(e,t=1,n=90,r=.005)=>{let i=hi(t=>e(t).gl()[0],0,t,r),a=hi(t=>e(t).gl()[1],0,t,r),o=hi(t=>e(t).gl()[2],0,t,r);return`linear-gradient(${n}deg, ${Array.from(new Set([...i.map(e=>gi(e[0])),...a.map(e=>gi(e[0])),...o.map(e=>gi(e[0]))].sort((e,t)=>e-t))).map(t=>`${e(t).hex()} ${gi(t*100)}%`).join()});`},vi=e=>{e.Color.prototype.jch=function(){return si(this._rgb.slice(0,3).map(e=>e/255))},e.jch=(...t)=>new e.Color(...oi(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.jab=function(){return di(this._rgb.slice(0,3).map(e=>e/255))},e.jab=(...t)=>new e.Color(...ui(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.hsluv=function(){return ni(this._rgb.slice(0,3).map(e=>e/255))},e.hsluv=(...t)=>new e.Color(...ri(t).map(e=>Math.floor(e*255)),`rgb`);let t=e.interpolate,n={jch:si,jab:di,hsluv:ni},r=(e,t,n)=>(Math.abs(e-t)>360/2&&(e>t?t+=360:e+=360),((1-n)*e+n*t)%360);e.interpolate=(i,a,o=.5,s=`lrgb`)=>{if(n[s]){typeof i!=`object`&&(i=new e.Color(i)),typeof a!=`object`&&(a=new e.Color(a));let t=n[s](i.gl()),c=n[s](a.gl()),l=Number.isNaN(i.hsl()[0]),u=Number.isNaN(a.hsl()[0]),d,f,p;switch(s){case`hsluv`:t[1]<1e-10&&(t[0]=c[0]),t[1]===0&&(t[1]=c[1]),c[1]<1e-10&&(c[0]=t[0]),c[1]===0&&(c[1]=t[1]),d=r(t[0],c[0],o),f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o;break;case`jch`:l&&(t[2]=c[2]),u&&(c[2]=t[2]),d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=r(t[2],c[2],o);break;default:d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o}return e[s](d,f,p).alpha(i.alpha()+o*(a.alpha()-i.alpha()))}return t(i,a,o,s)},e.getCSSGradient=_i},X={mainTRC:2.4,get mainTRCencode(){return 1/this.mainTRC},sRco:.2126729,sGco:.7151522,sBco:.072175,normBG:.56,normTXT:.57,revTXT:.62,revBG:.65,blkThrs:.022,blkClmp:1.414,scaleBoW:1.14,scaleWoB:1.14,loBoWoffset:.027,loWoBoffset:.027,deltaYmin:5e-4,loClip:.1,mFactor:1.9468554433171,get mFactInv(){return 1/this.mFactor},mOffsetIn:.0387393816571401,mExpAdj:.283343396420869,get mExp(){return this.mExpAdj/this.blkClmp},mOffsetOut:.312865795870758};function yi(e,t,n=-1){let r=[0,1.1];if(isNaN(e)||isNaN(t)||Math.min(e,t)r[1])return 0;let i=0,a=0,o=`BoW`;return e=e>X.blkThrs?e:e+(X.blkThrs-e)**+X.blkClmp,t=t>X.blkThrs?t:t+(X.blkThrs-t)**+X.blkClmp,Math.abs(t-e)e?(i=(t**+X.normBG-e**+X.normTXT)*X.scaleBoW,a=i-X.loClip?0:i+X.loWoBoffset),n<0?a*100:n==0?Math.round(Math.abs(a)*100)+``+o+``:Number.isInteger(n)?(a*100).toFixed(n):0)}function bi(e=[0,0,0]){function t(e){return(e/255)**X.mainTRC}return X.sRco*t(e[0])+X.sGco*t(e[1])+X.sBco*t(e[2])}var xi=(e,t,n,r,i,a,o,s,c)=>{let l=1-c,u=l*l,d=u*l,f=c*c*c;return{x:d*e+u*3*c*n+l*3*c*c*i+f*o,y:d*t+u*3*c*r+l*3*c*c*a+f*s}},Si=(e,t)=>{let n=[],r={x:+e[0],y:+e[1]};for(let i=0,a=e.length;a-2*!t>i;i+=2){let o=[{x:+e[i-2],y:+e[i-1]},{x:+e[i],y:+e[i+1]},{x:+e[i+2],y:+e[i+3]},{x:+e[i+4],y:+e[i+5]}];t?i?a-4===i?o[3]={x:+e[0],y:+e[1]}:a-2===i&&(o[2]={x:+e[0],y:+e[1]},o[3]={x:+e[2],y:+e[3]}):o[0]={x:+e[a-2],y:+e[a-1]}:a-4===i?o[3]=o[2]:i||(o[0]={x:+e[i],y:+e[i+1]}),n.push([r.x,r.y,(-o[0].x+6*o[1].x+o[2].x)/6,(-o[0].y+6*o[1].y+o[2].y)/6,(o[1].x+6*o[2].x-o[3].x)/6,(o[1].y+6*o[2].y-o[3].y)/6,o[2].x,o[2].y]),r=o[2]}return n},Ci=(e,t,n,r,i,a,o,s)=>{let c=e,l=t,u=0;for(let d=1;d<5;d++){let{x:f,y:p}=xi(e,t,n,r,i,a,o,s,d/5);u+=Math.hypot(f-c,p-l),c=f,l=p}return u+=Math.hypot(o-c,s-l),u},wi=(e,t,n,r,i,a,o,s)=>{let c=Math.floor(Ci(e,t,n,r,i,a,o,s)*.75),l=[],u=0;for(let d=0;d<=c;d++){let f=xi(e,t,n,r,i,a,o,s,d/c),p=Math.round(f.x);if(l[p]=f.y,p-u>1){let e=l[u],t=l[p];for(let n=u+1;nl[Math.round(e)]||null},Ti={CAM02:`jab`,CAM02p:`jch`,HEX:`hex`,HSL:`hsl`,HSLuv:`hsluv`,HSV:`hsv`,LAB:`lab`,LCH:`lch`,RGB:`rgb`,OKLAB:`oklab`,OKLCH:`oklch`};function Z(e,t=0){let n=10**t;return Math.round(e*n)/n}function Ei(e,t){let n;return n=e>1?(e-1)*t+1:e<-1?(e+1)*t-1:1,Z(n,2)}function Di(e){return K(String(e)).jch()}function Oi(e){return K(String(e)).hsluv()}function ki(e,t,n){let r=[[],[],[]];if(e.forEach((e,n)=>r.forEach((r,i)=>r.push(t[n],e[i]))),n===`hcl`){let e=r[1];for(let t=1;t{let t=[];for(let n=1;n{e[t]=e[n]}),t.length=0;break}if(t.length){let n=K(`#ccc`).jch()[2];t.forEach(t=>{e[t]=n})}t.length=0;for(let n=e.length-1;n>0;n-=2)if(Number.isNaN(e[n]))t.push(n);else{t.forEach(t=>{e[t]=e[n]});break}for(let t=1;tSi(e).map(e=>wi(...e)));return e=>{let t=i.map(t=>{for(let n=0;nr*t**e+i}function ji({swatches:e,colorKeys:t,colorspace:n,colorSpace:r=n??`LAB`,shift:i=1,fullScale:a=!0,smooth:o=!1,distributeLightness:s=`linear`,sortColor:c=!0,asFun:l=!1}={}){n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead.");let u=Ti[r];if(!u)throw Error(`Colorspace “${r}” not supported`);if(!t)throw Error(`Colorkeys missing: returned “${t}”`);let d;if(a)d=t.map(t=>e-e*(K(t).jch()[0]/100)).sort((e,t)=>e-t).concat(e),d.unshift(0);else{let n=t.map(e=>K(e).jch()[0]/100),r=Math.min(...n),i=Math.max(...n);d=n.map(t=>t===0||isNaN((t-r)/(i-r))?0:e-(t-r)/(i-r)*e).sort((e,t)=>e-t)}let f=Ai(i,[1,e],[1,e]);if(f=d.map(e=>Math.max(0,f(e))),d=f,s===`polynomial`){let t=e=>Math.sqrt(Math.sqrt((e**2.25+e**4)/2));d=f.map(t=>t/e).map(n=>t(n)*e)}let p=t.map((e,t)=>({colorKeys:Di(e),index:t})).sort((e,t)=>t.colorKeys[0]-e.colorKeys[0]).map(e=>t[e.index]),m=[],h;if(a){let e=u===`lch`?K.lch(...K(`#fff`).lch()):`#ffffff`,t=u===`lch`?K.lch(...K(`#000`).lch()):`#000000`;m=[e,...p,t]}else m=c?p:t;let g;if(o){let t=m;if(m=m.map(e=>K(String(e))[u]()),u===`hcl`&&m.forEach(e=>{e[1]=Number.isNaN(e[1])?0:e[1]}),u===`jch`)for(let e=0;eh(t))}else h=K.scale(m.map(e=>typeof e==`object`&&e.constructor===K.Color?e:String(e))).domain(d).mode(u);return l?h:(!o||o===!1?h.colors(e):g).filter(e=>e!=null)}function Mi(e,t){let n=[],r={};return Object.keys(e).forEach(n=>{r[e[n][t]]=e[n]}),Object.keys(r).forEach(e=>n.push(r[e])),n}function Ni(e){return Number.isNaN(e)?0:e}function Pi(e,t,n=!1){if(!e)throw Error(`Cannot convert color value of “${e}”`);if(!Ti[t])throw Error(`Cannot convert to colorspace “${t}”`);let r=Ti[t],i=K(String(e))[r]();if(t===`HSL`&&i.pop(),t===`HEX`){if(n){let t=K(String(e)).rgb();return{r:t[0],g:t[1],b:t[2]}}return i}let a={},o=i.map(Ni);o=o.map((e,t)=>{let i=Z(e),o=t;r===`hsluv`&&(o+=2);let s=r.charAt(o);return r===`jch`&&s===`c`&&(s=`C`),a[s===`j`?`J`:s]=i,r in{lab:1,lch:1,jab:1,jch:1}?n||(s===`l`||s===`j`)&&(i+=`%`):r!==`hsluv`&&(s===`s`||s===`l`||s===`v`)&&(a[s]=Z(e,2),n||(i=Z(e*100),i+=`%`)),i});let s=`${r}(${o.join(`, `)})`;return n?a:s}function Fi(e,t,n){let r=[e,t,n].map(e=>(e/=255,e<=.03928?e/12.92:((e+.055)/1.055)**2.4));return r[0]*.2126+r[1]*.7152+r[2]*.0722}function Ii(e,t,n,r=`wcag2`){if(n===void 0){let e=K.rgb(...t).hsluv()[2];n=Z(e/100,2)}if(r===`wcag2`){let r=Fi(e[0],e[1],e[2]),i=Fi(t[0],t[1],t[2]),a=(r+.05)/(i+.05),o=(i+.05)/(r+.05);return n<.5?a>=1?a:-o:a<1?o:a===1?a:-a}else if(r===`wcag3`)return n<.5?yi(bi(e),bi(t))*-1:yi(bi(e),bi(t));else throw Error(`Contrast calculation method ${r} unsupported; use 'wcag2' or 'wcag3'`)}function Li(e,t){if(!e)throw Error(`Array undefined`);if(!Array.isArray(e))throw Error(`Passed object is not an array`);let n=t===`wcag2`?0:1;return Math.min(...e.filter(e=>e>=n))}function Ri(e,t){if(!e)throw Error(`Ratios undefined`);e=e.sort((e,t)=>e-t);let n=Li(e,t),r=e.indexOf(n),i=[],a=e.slice(0,r),o=e.slice(r,e.length);for(let e=0;ee-t),i}var zi=(e,t,n,r,i)=>{let a=3e3,o=ji({swatches:a,colorKeys:e._modifiedKeys,colorspace:e._colorspace,shift:1,smooth:e._smooth,asFun:!0}),s={},c=e=>{if(s[e])return s[e];let r=Ii(K(o(e)).rgb(),t,n,i);return s[e]=r,r},l=e=>{let t=c(0).01&&o;)o--,n/=2,iu.push(o(l(+e)))),u},Q=class{constructor({name:e,colorKeys:t,colorspace:n,colorSpace:r=n??`RGB`,ratios:i,smooth:a=!1,output:o=`HEX`,saturation:s=100}){if(n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),this._name=e,this._colorKeys=t,this._modifiedKeys=t,this._colorspace=r,this._ratios=i,this._smooth=a,this._output=o,this._saturation=s,!this._name)throw Error(`Color missing name`);if(!this._colorKeys)throw Error(`Color Keys are undefined`);if(!Ti[this._colorspace])throw Error(`Colorspace “${r}” not supported`);if(!Ti[this._output])throw Error(`Output “${this._output}” not supported`);for(let e=0;e{let n=K(`${t}`).oklch(),r=n[1]*(this._saturation/100),i=K.oklch(n[0],r,n[2]),a=K.rgb(i).hex();e.push(a)}),this._modifiedKeys=e,this._generateColorScale()}_generateColorScale(){this._colorScale=ji({swatches:3e3,colorKeys:this._modifiedKeys,colorSpace:this._colorspace,shift:1,smooth:this._smooth,asFun:!0})}},Bi=class extends Q{get backgroundColorScale(){return this._backgroundColorScale||this._generateColorScale(),this._backgroundColorScale}_generateColorScale(){Q.prototype._generateColorScale.call(this);let e=ji({swatches:1e3,colorKeys:this._colorKeys,colorspace:this._colorspace,shift:1,smooth:this._smooth});e.push(...this.colorKeys);let t=Mi(e.map((e,t)=>({value:Math.round(Oi(e)[2]),index:t})),`value`).map(t=>e[t.index]);return t.length>=101&&(t.length=100,t.push(`#ffffff`)),this._backgroundColorScale=t.map(e=>Pi(e,this._output)),this._backgroundColorScale}},Vi=class{constructor({colors:e,backgroundColor:t,lightness:n,contrast:r=1,saturation:i=100,output:a=`HEX`,formula:o=`wcag2`}){if(this._output=a,this._colors=e,this._lightness=n,this._saturation=i,this._formula=o,this._setBackgroundColor(t),this._setBackgroundColorValue(),this._contrast=r,!this._colors)throw Error(`No colors are defined`);if(!this._backgroundColor)throw Error(`Background color is undefined`);if(e.forEach(e=>{if(!e.ratios)throw Error(`Color ${e.name}'s ratios are undefined`)}),!Ti[this._output])throw Error(`Output “${a}” not supported`);this._saturation<100&&this._updateColorSaturation(this._saturation),this._findContrastColors(),this._findContrastColorPairs(),this._findContrastColorValues()}set formula(e){this._formula=e,this._findContrastColors()}get formula(){return this._formula}set contrast(e){this._contrast=e,this._findContrastColors()}get contrast(){return this._contrast}set lightness(e){this._lightness=e,this._setBackgroundColor(this._backgroundColor),this._findContrastColors()}get lightness(){return this._lightness}set saturation(e){this._saturation=e,this._updateColorSaturation(e),this._findContrastColors()}get saturation(){return this._saturation}set backgroundColor(e){this._setBackgroundColor(e),this._findContrastColors()}get backgroundColorValue(){return this._backgroundColorValue}get backgroundColor(){return this._backgroundColor}set colors(e){this._colors=e,this._findContrastColors()}get colors(){return this._colors}set addColor(e){this._colors.push(e),this._findContrastColors()}set removeColor(e){let t=this._colors.filter(t=>t.name!==e.name);this._colors=t,this._findContrastColors()}set updateColor(e){if(Array.isArray(e))for(let t=0;tn.name===e[t].color);n=n[0];let r=this._colors.indexOf(n),i=this._colors.filter(n=>n.name!==e[t].color);e[t].name&&(n.name=e[t].name),e[t].colorKeys&&(n.colorKeys=e[t].colorKeys),e[t].ratios&&(n.ratios=e[t].ratios),(e[t].colorSpace!==void 0||e[t].colorspace!==void 0)&&(e[t].colorspace!==void 0&&e[t].colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),n.colorSpace=e[t].colorSpace??e[t].colorspace),e[t].smooth&&(n.smooth=e[t].smooth),n._generateColorScale(),i.splice(r,0,n),this._colors=i}else{let t=this._colors.filter(t=>t.name===e.color);t=t[0];let n=this._colors.indexOf(t),r=this._colors.filter(t=>t.name!==e.color);e.name&&(t.name=e.name),e.colorKeys&&(t.colorKeys=e.colorKeys),e.ratios&&(t.ratios=e.ratios),(e.colorSpace!==void 0||e.colorspace!==void 0)&&(e.colorspace!==void 0&&e.colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),t.colorSpace=e.colorSpace??e.colorspace),e.smooth&&(t.smooth=e.smooth),t._generateColorScale(),r.splice(n,0,t),this._colors=r}this._findContrastColors()}set output(e){this._output=e,this._colors.forEach(e=>{e.output=this._output}),this._backgroundColor.output=this._output,this._findContrastColors()}get output(){return this._output}get contrastColors(){return this._contrastColors}get contrastColorPairs(){return this._contrastColorPairs}get contrastColorValues(){return this._contrastColorValues}_setBackgroundColor(e){if(typeof e==`string`){let t=new Bi({name:`background`,colorKeys:[e],output:`RGB`}),n=Z(K(String(e)).hsluv()[2]);this._backgroundColor=t,this._lightness=n,this._backgroundColorValue=t[this._lightness]}else{e.output=`RGB`;let t=e.backgroundColorScale[this._lightness];this._backgroundColor=e,this._backgroundColorValue=t}}_setBackgroundColorValue(){this._backgroundColorValue=this._backgroundColor.backgroundColorScale[this._lightness]}_updateColorSaturation(e){this._colors.map(t=>{t.saturation=e})}_findContrastColors(){let e=K(String(this._backgroundColorValue)).rgb(),t=this._lightness/100,n={background:Pi(this._backgroundColorValue,this._output)},r=[],i=[],a={...n};return r.push(n),this._colors.map(n=>{if(n.ratios!==void 0){let o,s=[],c={name:n.name,values:s},l;Array.isArray(n.ratios)?l=n.ratios:Array.isArray(n.ratios)||(o=Object.keys(n.ratios),l=Object.values(n.ratios)),l=l.map(e=>Ei(+e,this._contrast));let u=zi(n,e,t,l,this._formula).map(e=>Pi(e,this._output));for(let e=0;e{let t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return[Number.parseInt(t[1],16),Number.parseInt(t[2],16),Number.parseInt(t[3],16)]},Wi=(e,t,n)=>{let r=e/255,i=t/255,a=n/255,o=Math.min(r,i,a),s=Math.max(r,i,a),c=s-o,l=0,u=0,d=0;return l=c===0?0:s===r?(i-a)/c%6:s===i?(a-r)/c+2:(r-i)/c+4,l=Math.round(l*60),l<0&&(l+=360),d=(s+o)/2,u=c===0?0:c/(1-Math.abs(2*d-1)),u=+(u*100).toFixed(1),d=+(d*100).toFixed(1),[l,u,Math.round(d)]},Gi=(e,t,n,r)=>{let i=n/100,a=t*Math.min(i,1-i)/100,o=t=>{let n=(t+e/30)%12,r=i-a*Math.max(Math.min(n-3,9-n,1),-1);return Math.round(255*r).toString(16).padStart(2,`0`).toUpperCase()},s=o(0),c=o(8),l=o(4),u=((e,t,n)=>Math.min(Math.max(e,t),n))(r,0,1);return`#${s}${c}${l}${Math.round(u*255).toString(16).padStart(2,`0`).toUpperCase()}`},Ki=(e,t,n=1)=>{let r=Ui(e),i=Ui(t===`white`?`#FFFFFF`:t===`black`?`#000000`:t),a=r.map((e,t)=>[(e-i[t])/(255-i[t]),(e-i[t])/(0-i[t])]),o=Hi(Math.max(...a.flat().filter(e=>/^-?\d+\.?\d*$/.test(e)))),s=r.map((e,t)=>Math.round((e-i[t]+i[t]*o)/o));if(s.includes(NaN)){let e=Wi(r[0],r[1],r[2]);return{h:e[0],s:Math.round(e[1]*n),l:e[2],a:1}}let c=Wi(s[0],s[1],s[2]);return{h:c[0],s:Math.round(c[1]*n),l:c[2],a:o}},qi={backgroundColor:`gray`,colorSpace:`OKLCH`,colorSmoothing:!1,formula:`wcag2`,output:`HEX`,colors:{gray:[$(215,20,90),$(215,8,50),$(215,6,25)],red:[$(358,100,58),$(350,100,30)],orange:[$(32,100,48),$(12,100,30)],yellow:[$(50,100,50),$(25,100,20)],lime:[$(100,68,50),$(115,86,25)],green:[$(163,87,42),$(168,100,25)],cyan:[$(185,80,45),$(200,98,35)],blue:[$(212,98,46),$(222,95,25)],purple:[$(258,94,64),$(265,100,35)],fuchsia:[$(295,56,50),$(285,80,25)],pink:[$(334,90,50),$(330,91,25)]},themes:{light:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16.75],contrast:1,lightness:100,saturation:100},dark:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16],contrast:1,lightness:6,saturation:97},lightHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:100,saturation:100},darkHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:6,saturation:97}}};function $(e,t,n){return K.hsl(e,t/100,n/100).hex()}function Ji(e,t){let n=e.colorSpace,r=e.colorSmoothing,i=e.themes[t].ratios,a=new Bi({name:`gray`,colorKeys:e.colors.gray,colorspace:n,ratios:i,smooth:r}),o=new Q({name:`blue`,colorKeys:e.colors.blue,colorspace:n,ratios:i,smooth:r}),s=new Q({name:`cyan`,colorKeys:e.colors.cyan,colorspace:n,ratios:i,smooth:r}),c=new Q({name:`fuchsia`,colorKeys:e.colors.fuchsia,colorspace:n,ratios:i,smooth:r}),l=new Q({name:`green`,colorKeys:e.colors.green,colorspace:n,ratios:i,smooth:r}),u=new Q({name:`lime`,colorKeys:e.colors.lime,colorspace:n,ratios:i,smooth:r}),d=new Q({name:`orange`,colorKeys:e.colors.orange,colorspace:n,ratios:i,smooth:r}),f=new Q({name:`pink`,colorKeys:e.colors.pink,colorspace:n,ratios:i,smooth:r}),p=new Q({name:`purple`,colorKeys:e.colors.purple,colorspace:n,ratios:i,smooth:r}),m={gray:a,red:new Q({name:`red`,colorKeys:e.colors.red,colorspace:n,ratios:i,smooth:r}),orange:d,yellow:new Q({name:`yellow`,colorKeys:e.colors.yellow,colorspace:n,ratios:i,smooth:r}),lime:u,green:l,cyan:s,blue:o,purple:p,fuchsia:c,pink:f};return e.colors.custom&&(m.custom=new Q({name:`custom`,colorKeys:e.colors.custom,colorspace:n,ratios:i,smooth:r})),new Vi({colors:Object.values(m),backgroundColor:m[e.backgroundColor],contrast:e.themes[t].contrast,lightness:e.themes[t].lightness,saturation:e.themes[t].saturation,output:e.output,formula:e.formula}).contrastColors}function Yi(e){let t={};for(let n of Object.keys(e.themes))t[n]=Ji(e,n);return t}function Xi(e){qi.colors.custom=[e];let t=Yi(qi);return Object.fromEntries(Object.entries(t).map(([e,t])=>{let n=t.find(e=>e&&e.name===`custom`),r=Object.fromEntries(n.values.map(({name:e,value:t})=>[e,t]));for(let[e,n]of Object.entries(r)){let i=Ki(n,t[0].background);r[`alpha${e.charAt(0).toUpperCase()+e.slice(1)}`]=Gi(i.h,i.s,i.l,i.a)}return[e,r]}))}return e.generateCustomColors=Xi,e.generateThemesJson=Yi,e.hslToHex=$,e.leonardoConfig=qi,e})({}); \ No newline at end of file diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt index 5851a758f0..d8f81dbbbb 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/CompoundIcons.kt @@ -130,6 +130,9 @@ object CompoundIcons { @Composable fun Collapse(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_collapse) } + @Composable fun CollapseAll(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_collapse_all) + } @Composable fun Company(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_company) } @@ -142,6 +145,9 @@ object CompoundIcons { @Composable fun Copy(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_copy) } + @Composable fun Crop(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_crop) + } @Composable fun DarkMode(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_dark_mode) } @@ -202,6 +208,9 @@ object CompoundIcons { @Composable fun Expand(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_expand) } + @Composable fun ExpandAll(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_expand_all) + } @Composable fun Explore(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_explore) } @@ -229,6 +238,15 @@ object CompoundIcons { @Composable fun Filter(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_filter) } + @Composable fun FlipHorizontal(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_flip_horizontal) + } + @Composable fun FlipVertical(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_flip_vertical) + } + @Composable fun Folder(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_folder) + } @Composable fun Forward(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_forward) } @@ -496,6 +514,12 @@ object CompoundIcons { @Composable fun RotateRight(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_rotate_right) } + @Composable fun Save(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_save) + } + @Composable fun SaveSolid(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_save_solid) + } @Composable fun Search(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_search) } @@ -607,6 +631,9 @@ object CompoundIcons { @Composable fun Unpin(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_unpin) } + @Composable fun Unsave(): ImageVector { + return ImageVector.vectorResource(R.drawable.ic_compound_unsave) + } @Composable fun User(): ImageVector { return ImageVector.vectorResource(R.drawable.ic_compound_user) } @@ -735,10 +762,12 @@ object CompoundIcons { CloudSolid(), Code(), Collapse(), + CollapseAll(), Company(), Compose(), Computer(), Copy(), + Crop(), DarkMode(), Delete(), DevicePasskey(), @@ -759,6 +788,7 @@ object CompoundIcons { ErrorSolid(), ExitFullScreen(), Expand(), + ExpandAll(), Explore(), ExportArchive(), Extensions(), @@ -768,6 +798,9 @@ object CompoundIcons { FileError(), Files(), Filter(), + FlipHorizontal(), + FlipVertical(), + Folder(), Forward(), FullScreen(), Grid(), @@ -857,6 +890,8 @@ object CompoundIcons { Room(), RotateLeft(), RotateRight(), + Save(), + SaveSolid(), Search(), Section(), Send(), @@ -894,6 +929,7 @@ object CompoundIcons { Unknown(), UnknownSolid(), Unpin(), + Unsave(), User(), UserAdd(), UserAddSolid(), @@ -963,10 +999,12 @@ object CompoundIcons { R.drawable.ic_compound_cloud_solid, R.drawable.ic_compound_code, R.drawable.ic_compound_collapse, + R.drawable.ic_compound_collapse_all, R.drawable.ic_compound_company, R.drawable.ic_compound_compose, R.drawable.ic_compound_computer, R.drawable.ic_compound_copy, + R.drawable.ic_compound_crop, R.drawable.ic_compound_dark_mode, R.drawable.ic_compound_delete, R.drawable.ic_compound_device_passkey, @@ -987,6 +1025,7 @@ object CompoundIcons { R.drawable.ic_compound_error_solid, R.drawable.ic_compound_exit_full_screen, R.drawable.ic_compound_expand, + R.drawable.ic_compound_expand_all, R.drawable.ic_compound_explore, R.drawable.ic_compound_export_archive, R.drawable.ic_compound_extensions, @@ -996,6 +1035,9 @@ object CompoundIcons { R.drawable.ic_compound_file_error, R.drawable.ic_compound_files, R.drawable.ic_compound_filter, + R.drawable.ic_compound_flip_horizontal, + R.drawable.ic_compound_flip_vertical, + R.drawable.ic_compound_folder, R.drawable.ic_compound_forward, R.drawable.ic_compound_full_screen, R.drawable.ic_compound_grid, @@ -1085,6 +1127,8 @@ object CompoundIcons { R.drawable.ic_compound_room, R.drawable.ic_compound_rotate_left, R.drawable.ic_compound_rotate_right, + R.drawable.ic_compound_save, + R.drawable.ic_compound_save_solid, R.drawable.ic_compound_search, R.drawable.ic_compound_section, R.drawable.ic_compound_send, @@ -1122,6 +1166,7 @@ object CompoundIcons { R.drawable.ic_compound_unknown, R.drawable.ic_compound_unknown_solid, R.drawable.ic_compound_unpin, + R.drawable.ic_compound_unsave, R.drawable.ic_compound_user, R.drawable.ic_compound_user_add, R.drawable.ic_compound_user_add_solid, diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt index f5a54bb7be..6bc6c89a63 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColors.kt @@ -175,6 +175,8 @@ data class SemanticColors( val iconTertiaryAlpha: Color, /** Used to separate core sections of the UI as well as containers */ val separatorPrimary: Color, + /** Secondary shade for separating sections of components or list items */ + val separatorSecondary: Color, /** Accent text colour for plain actions. */ val textActionAccent: Color, /** Default text colour for plain actions. */ diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt index 35a1234854..e9334de0e7 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDark.kt @@ -103,6 +103,7 @@ val compoundColorsDark = SemanticColors( iconTertiary = DarkColorTokens.colorGray800, iconTertiaryAlpha = DarkColorTokens.colorAlphaGray800, separatorPrimary = DarkColorTokens.colorGray400, + separatorSecondary = DarkColorTokens.colorGray300, textActionAccent = DarkColorTokens.colorGreen900, textActionPrimary = DarkColorTokens.colorGray1400, textBadgeAccent = DarkColorTokens.colorGreen1100, diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt index 802e3d56b0..a3ef51cff1 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsDarkHc.kt @@ -103,6 +103,7 @@ val compoundColorsHcDark = SemanticColors( iconTertiary = DarkHcColorTokens.colorGray800, iconTertiaryAlpha = DarkHcColorTokens.colorAlphaGray800, separatorPrimary = DarkHcColorTokens.colorGray400, + separatorSecondary = DarkHcColorTokens.colorGray300, textActionAccent = DarkHcColorTokens.colorGreen900, textActionPrimary = DarkHcColorTokens.colorGray1400, textBadgeAccent = DarkHcColorTokens.colorGreen1100, diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt index 166f9ddafc..75153cb66b 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLight.kt @@ -103,6 +103,7 @@ val compoundColorsLight = SemanticColors( iconTertiary = LightColorTokens.colorGray800, iconTertiaryAlpha = LightColorTokens.colorAlphaGray800, separatorPrimary = LightColorTokens.colorGray400, + separatorSecondary = LightColorTokens.colorGray300, textActionAccent = LightColorTokens.colorGreen900, textActionPrimary = LightColorTokens.colorGray1400, textBadgeAccent = LightColorTokens.colorGreen1100, diff --git a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt index 8914b41bfc..fa40a2e2ab 100644 --- a/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt +++ b/libraries/compound/src/main/kotlin/io/element/android/compound/tokens/generated/SemanticColorsLightHc.kt @@ -103,6 +103,7 @@ val compoundColorsHcLight = SemanticColors( iconTertiary = LightHcColorTokens.colorGray800, iconTertiaryAlpha = LightHcColorTokens.colorAlphaGray800, separatorPrimary = LightHcColorTokens.colorGray400, + separatorSecondary = LightHcColorTokens.colorGray300, textActionAccent = LightHcColorTokens.colorGreen900, textActionPrimary = LightHcColorTokens.colorGray1400, textBadgeAccent = LightHcColorTokens.colorGreen1100, diff --git a/libraries/compound/src/main/res/drawable/ic_compound_collapse_all.xml b/libraries/compound/src/main/res/drawable/ic_compound_collapse_all.xml new file mode 100644 index 0000000000..7257b7d2c3 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_collapse_all.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_crop.xml b/libraries/compound/src/main/res/drawable/ic_compound_crop.xml new file mode 100644 index 0000000000..dc9513a239 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_crop.xml @@ -0,0 +1,12 @@ + + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_expand_all.xml b/libraries/compound/src/main/res/drawable/ic_compound_expand_all.xml new file mode 100644 index 0000000000..fcef02880b --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_expand_all.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_flip_horizontal.xml b/libraries/compound/src/main/res/drawable/ic_compound_flip_horizontal.xml new file mode 100644 index 0000000000..90a3a98f44 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_flip_horizontal.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_flip_vertical.xml b/libraries/compound/src/main/res/drawable/ic_compound_flip_vertical.xml new file mode 100644 index 0000000000..430e5eb3fa --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_flip_vertical.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_save.xml b/libraries/compound/src/main/res/drawable/ic_compound_save.xml new file mode 100644 index 0000000000..3bedcc272d --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_save.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_save_solid.xml b/libraries/compound/src/main/res/drawable/ic_compound_save_solid.xml new file mode 100644 index 0000000000..5d453834c7 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_save_solid.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/compound/src/main/res/drawable/ic_compound_unsave.xml b/libraries/compound/src/main/res/drawable/ic_compound_unsave.xml new file mode 100644 index 0000000000..0dfddcfe46 --- /dev/null +++ b/libraries/compound/src/main/res/drawable/ic_compound_unsave.xml @@ -0,0 +1,13 @@ + + + + diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png index 79992a9708..9c80a760b7 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30b39281fd597089c94c8ff1680297143989d6da97d6f5b15198be5f92469483 -size 114653 +oid sha256:787bcdf784abedc6858fdab6c7b5dac9e36d42a1047ac0bad181449dbb757585 +size 117936 From a9ecf3c5dcd263e49811ebc2990e66d26b994f99 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 28 May 2026 15:00:03 +0200 Subject: [PATCH 323/471] Add flip actions to image edition --- .../preview/AttachmentsPreviewEvent.kt | 2 + .../preview/AttachmentsPreviewPresenter.kt | 12 ++++ .../preview/AttachmentsPreviewView.kt | 2 + .../imageeditor/AttachmentImageEditor.kt | 37 ++++++---- .../imageeditor/AttachmentImageEditorState.kt | 47 ++++++++++-- .../AttachmentImageEditorStateProvider.kt | 10 +++ .../imageeditor/AttachmentImageEditorView.kt | 72 +++++++++++++++---- .../impl/src/main/res/values/localazy.xml | 6 ++ .../AttachmentsPreviewPresenterTest.kt | 35 ++++++++- .../imageeditor/AttachmentImageEditsTest.kt | 33 +++++++++ 10 files changed, 219 insertions(+), 37 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt index a7c64845d1..fb9fddbb48 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvent.kt @@ -17,6 +17,8 @@ sealed interface AttachmentsPreviewEvent { data object OpenImageEditor : AttachmentsPreviewEvent data object CloseImageEditor : AttachmentsPreviewEvent data object RotateImageToTheLeft : AttachmentsPreviewEvent + data object FlipImageHorizontally : AttachmentsPreviewEvent + data object FlipImageVertically : AttachmentsPreviewEvent data object ApplyImageEdits : AttachmentsPreviewEvent data object ResetImageEdits : AttachmentsPreviewEvent data class UpdateImageCropRect(val cropRect: NormalizedCropRect) : AttachmentsPreviewEvent 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 cab00d99c1..0f60d7ff0e 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 @@ -295,6 +295,18 @@ class AttachmentsPreviewPresenter( edits = pendingState.edits.rotateAntiClockwise() ) } + AttachmentsPreviewEvent.FlipImageHorizontally -> { + val pendingState = imageEditorState ?: return + imageEditorState = pendingState.copy( + edits = pendingState.edits.flipHorizontally() + ) + } + AttachmentsPreviewEvent.FlipImageVertically -> { + val pendingState = imageEditorState ?: return + imageEditorState = pendingState.copy( + edits = pendingState.edits.flipVertically() + ) + } AttachmentsPreviewEvent.ResetImageEdits -> { imageEditorState = imageEditorState?.copy( edits = AttachmentImageEdits() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 1714a8b8dc..926106a5ee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -136,6 +136,8 @@ fun AttachmentsPreviewView( state.eventSink(AttachmentsPreviewEvent.UpdateImageCropRect(cropRect)) }, onRotateClick = { state.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) }, + onFlipHorizontallyClick = { state.eventSink(AttachmentsPreviewEvent.FlipImageHorizontally) }, + onFlipVerticallyClick = { state.eventSink(AttachmentsPreviewEvent.FlipImageVertically) }, onCancelClick = ::postCloseImageEditor, onResetClick = ::postResetImageEditor, onDoneClick = ::postApplyImageEdits, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt index cb66802184..f8d199a74e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditor.kt @@ -87,30 +87,30 @@ class DefaultAttachmentImageEditor( decodedBitmap.recycle() } - val rotatedBitmap = normalizedBitmap.rotateQuarterTurns(edits.rotationQuarterTurns) - if (rotatedBitmap !== normalizedBitmap) { + val transformedBitmap = normalizedBitmap.applyEdits(edits) + if (transformedBitmap !== normalizedBitmap) { normalizedBitmap.recycle() } val cropRect = edits.cropRect.toPixelRect( - imageWidth = rotatedBitmap.width, - imageHeight = rotatedBitmap.height, + imageWidth = transformedBitmap.width, + imageHeight = transformedBitmap.height, ) val isCropUnchanged = cropRect.left == 0 && cropRect.top == 0 && - cropRect.width() == rotatedBitmap.width && cropRect.height() == rotatedBitmap.height + cropRect.width() == transformedBitmap.width && cropRect.height() == transformedBitmap.height val croppedBitmap = if (isCropUnchanged) { - rotatedBitmap + transformedBitmap } else { Bitmap.createBitmap( - rotatedBitmap, + transformedBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height(), ) } - if (croppedBitmap !== rotatedBitmap) { - rotatedBitmap.recycle() + if (croppedBitmap !== transformedBitmap) { + transformedBitmap.recycle() } val editedMediaDir = File(context.cacheDir, EDITED_MEDIA_DIR_NAME).apply { mkdirs() } @@ -141,11 +141,22 @@ internal fun exportedMimeTypeFor(sourceMimeType: String?): String { } } -private fun Bitmap.rotateQuarterTurns(quarterTurns: Int): Bitmap { - val normalizedTurns = (quarterTurns % 4 + 4) % 4 - if (normalizedTurns == 0) return this +private fun Bitmap.applyEdits(edits: AttachmentImageEdits): Bitmap { + val normalizedTurns = (edits.rotationQuarterTurns % 4 + 4) % 4 + if (normalizedTurns == 0 && !edits.isFlippedHorizontally && !edits.isFlippedVertically) { + return this + } + val centerX = width / 2f + val centerY = height / 2f val matrix = Matrix().apply { - postRotate(normalizedTurns * 90f) + val scaleX = if (edits.isFlippedHorizontally) -1f else 1f + val scaleY = if (edits.isFlippedVertically) -1f else 1f + if (scaleX < 0f || scaleY < 0f) { + postScale(scaleX, scaleY, centerX, centerY) + } + if (normalizedTurns != 0) { + postRotate(normalizedTurns * 90f, centerX, centerY) + } } return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt index 3c8af52ce8..b798df2747 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorState.kt @@ -26,6 +26,8 @@ data class AttachmentImageEditorState( data class AttachmentImageEdits( val cropRect: NormalizedCropRect = NormalizedCropRect.default(), val rotationQuarterTurns: Int = 0, + val isFlippedHorizontally: Boolean = false, + val isFlippedVertically: Boolean = false, ) { val normalizedRotationQuarterTurns: Int get() = rotationQuarterTurns % 4 @@ -34,18 +36,32 @@ data class AttachmentImageEdits( get() = normalizedRotationQuarterTurns * 90 val hasChanges: Boolean - get() = cropRect != NormalizedCropRect.default() || normalizedRotationQuarterTurns != 0 + get() = cropRect != NormalizedCropRect.default() || + normalizedRotationQuarterTurns != 0 || + isFlippedHorizontally || + isFlippedVertically fun rotateAntiClockwise(): AttachmentImageEdits { return copy( rotationQuarterTurns = (normalizedRotationQuarterTurns + 3) % 4, // Also update the crop rect to keep the same selected area - cropRect = NormalizedCropRect( - left = cropRect.top, - top = 1f - cropRect.right, - right = cropRect.bottom, - bottom = 1f - cropRect.left, - ) + cropRect = cropRect.rotateAntiClockwise() + ) + } + + fun flipHorizontally(): AttachmentImageEdits { + return copy( + isFlippedHorizontally = !isFlippedHorizontally, + // Also update the crop rect to keep the same selected area + cropRect = cropRect.flipHorizontally(), + ) + } + + fun flipVertically(): AttachmentImageEdits { + return copy( + isFlippedVertically = !isFlippedVertically, + // Also update the crop rect to keep the same selected area + cropRect = cropRect.flipVertically(), ) } } @@ -135,6 +151,23 @@ data class NormalizedCropRect( ) } + fun rotateAntiClockwise() = copy( + left = top, + top = 1f - right, + right = bottom, + bottom = 1f - left, + ) + + fun flipHorizontally() = copy( + left = 1f - right, + right = 1f - left, + ) + + fun flipVertically() = copy( + top = 1f - bottom, + bottom = 1f - top, + ) + companion object { fun default() = NormalizedCropRect( left = DEFAULT_CROP_MARGIN, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt index df4bb5257f..7c542fd1a8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditorStateProvider.kt @@ -75,6 +75,16 @@ open class AttachmentImageEditorStateProvider : PreviewParameterProvider Unit, onRotateClick: () -> Unit, + onFlipHorizontallyClick: () -> Unit, + onFlipVerticallyClick: () -> Unit, onResetClick: () -> Unit, onCancelClick: () -> Unit, onDoneClick: () -> Unit, @@ -101,8 +101,18 @@ fun AttachmentImageEditorView( state.edits.rotationDegrees, state.edits.rotationDegrees, ) - val rotateButtonBackground = ElementTheme.colors.bgCanvasDefault - + val flipHorizontalLabel = stringResource(R.string.screen_image_edition_a11y_flip_image_horizontally) + val flipHorizontalState = if (state.edits.isFlippedHorizontally) { + stringResource(R.string.screen_image_edition_a11y_flip_image_horizontally_state_flipped) + } else { + stringResource(R.string.screen_image_edition_a11y_flip_image_horizontally_state_original) + } + val flipVerticalLabel = stringResource(R.string.screen_image_edition_a11y_flip_image_vertically) + val flipVerticalState = if (state.edits.isFlippedVertically) { + stringResource(R.string.screen_image_edition_a11y_flip_image_vertically_state_flipped) + } else { + stringResource(R.string.screen_image_edition_a11y_flip_image_vertically_state_original) + } Scaffold( modifier = modifier.fillMaxSize(), topBar = { @@ -158,37 +168,57 @@ fun AttachmentImageEditorView( onClick = onResetClick, ) } - Box( - modifier = Modifier.weight(1f), - contentAlignment = Alignment.Center, + Row( + modifier = Modifier.weight(2f), + // Center the content horizontally + horizontalArrangement = Arrangement.Center, ) { + IconButton( + onClick = onFlipHorizontallyClick, + modifier = Modifier + .clearAndSetSemantics { + contentDescription = flipHorizontalLabel + stateDescription = flipHorizontalState + } + ) { + Icon( + imageVector = CompoundIcons.FlipHorizontal(), + contentDescription = null, + ) + } IconButton( onClick = onRotateClick, modifier = Modifier - .background( - color = rotateButtonBackground, - shape = CircleShape, - ) - .border(1.dp, ElementTheme.colors.borderInteractiveSecondary, CircleShape) .clearAndSetSemantics { contentDescription = rotateContentDescription stateDescription = rotationStateDescription } ) { Icon( - modifier = Modifier - .size(22.dp), imageVector = CompoundIcons.RotateLeft(), contentDescription = null, ) } + IconButton( + onClick = onFlipVerticallyClick, + modifier = Modifier + .clearAndSetSemantics { + contentDescription = flipVerticalLabel + stateDescription = flipVerticalState + } + ) { + Icon( + imageVector = CompoundIcons.FlipVertical(), + contentDescription = null, + ) + } } Box( modifier = Modifier.weight(1f), contentAlignment = Alignment.CenterEnd, ) { TextButton( - text = stringResource(CommonStrings.action_done), + text = stringResource(CommonStrings.action_save), onClick = onDoneClick, ) } @@ -204,6 +234,8 @@ private fun BoxScope.CropEditorCanvas( ) { var imageSize by remember(state.localMedia.uri) { mutableStateOf(IntSize.Zero) } val rotationQuarterTurns = state.edits.normalizedRotationQuarterTurns + val flipScaleX = if (state.edits.isFlippedHorizontally) -1f else 1f + val flipScaleY = if (state.edits.isFlippedVertically) -1f else 1f var imageRect by remember { mutableStateOf(Rect.Zero) } @@ -257,6 +289,10 @@ private fun BoxScope.CropEditorCanvas( contentDescription = null, modifier = Modifier .requiredSize(imageLayoutWidthDp, imageLayoutHeightDp) + .graphicsLayer { + scaleX = flipScaleX + scaleY = flipScaleY + } .graphicsLayer { rotationZ = rotationQuarterTurns * 90f }, contentScale = ContentScale.Fit, ) @@ -266,6 +302,10 @@ private fun BoxScope.CropEditorCanvas( contentDescription = stringResource(CommonStrings.common_image), modifier = Modifier .requiredSize(imageLayoutWidthDp, imageLayoutHeightDp) + .graphicsLayer { + scaleX = flipScaleX + scaleY = flipScaleY + } .graphicsLayer { rotationZ = rotationQuarterTurns * 90f }, contentScale = ContentScale.Fit, onState = { painterState -> @@ -642,6 +682,8 @@ internal fun AttachmentImageEditorViewPreview( state = state, onCropRectChange = {}, onRotateClick = {}, + onFlipHorizontallyClick = {}, + onFlipVerticallyClick = {}, onResetClick = {}, onCancelClick = {}, onDoneClick = {}, diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index 908646b048..173a4e78ad 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -16,6 +16,12 @@ "Travel & Places" "Recent emojis" "Symbols" + "Flip image horizontally" + "Flipped horizontally" + "Original" + "Flip image vertically" + "Flipped vertically" + "Original" "Rotate the image to the left" "%1$d degree" 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 140d4f8ea3..817eb740d7 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 @@ -624,7 +624,11 @@ class AttachmentsPreviewPresenterTest { val croppedState = awaitItem() croppedState.eventSink(AttachmentsPreviewEvent.RotateImageToTheLeft) val rotatedState = awaitItem() - rotatedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) + rotatedState.eventSink(AttachmentsPreviewEvent.FlipImageHorizontally) + val flippedHorizontallyState = awaitItem() + flippedHorizontallyState.eventSink(AttachmentsPreviewEvent.FlipImageVertically) + val flippedState = awaitItem() + flippedState.eventSink(AttachmentsPreviewEvent.ApplyImageEdits) val appliedState = consumeItemsUntilPredicate { !it.isApplyingImageEdits && it.imageEditorState == null }.last() assertThat((appliedState.attachment as Attachment.Media).localMedia.uri).isEqualTo(editedUri) @@ -638,9 +642,36 @@ class AttachmentsPreviewPresenterTest { right = cropRect.bottom, bottom = 1f - cropRect.left, ) - reopenedState.imageEditorState.edits.cropRect.assertIsSimilarTo(rotatedCropRect) + val flippedCropRect = NormalizedCropRect( + left = 1f - rotatedCropRect.right, + top = 1f - rotatedCropRect.bottom, + right = 1f - rotatedCropRect.left, + bottom = 1f - rotatedCropRect.top, + ) + reopenedState.imageEditorState.edits.cropRect.assertIsSimilarTo(flippedCropRect) assertThat(reopenedState.imageEditorState.edits.rotationQuarterTurns).isEqualTo(3) assertThat(reopenedState.imageEditorState.edits.rotationDegrees).isEqualTo(270) + assertThat(reopenedState.imageEditorState.edits.isFlippedHorizontally).isTrue() + assertThat(reopenedState.imageEditorState.edits.isFlippedVertically).isTrue() + } + } + + @Test + fun `present - image editor flip events update edits`() = runTest { + val presenter = createAttachmentsPreviewPresenter(displayMediaQualitySelectorViews = true) + + presenter.test { + val initialState = awaitItem() + initialState.eventSink(AttachmentsPreviewEvent.OpenImageEditor) + val editorState = consumeItemsUntilPredicate { it.imageEditorState != null }.last() + + editorState.eventSink(AttachmentsPreviewEvent.FlipImageHorizontally) + val flippedHorizontallyState = awaitItem() + assertThat(flippedHorizontallyState.imageEditorState?.edits?.isFlippedHorizontally).isTrue() + + flippedHorizontallyState.eventSink(AttachmentsPreviewEvent.FlipImageVertically) + val flippedState = awaitItem() + assertThat(flippedState.imageEditorState?.edits?.isFlippedVertically).isTrue() } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt index 43c893dd05..eb989967ef 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/preview/imageeditor/AttachmentImageEditsTest.kt @@ -42,4 +42,37 @@ class AttachmentImageEditsTest { assertThat(result.cropRect.bottom).isWithin(0.0001f).of(0.8f) assertThat(result.hasChanges).isTrue() } + + @Test + fun `flip horizontally updates crop and change tracking`() { + val sut = AttachmentImageEdits( + cropRect = NormalizedCropRect( + left = 0.1f, + top = 0.3f, + right = 0.6f, + bottom = 0.9f, + ) + ) + val result = sut.flipHorizontally() + assertThat(result.isFlippedHorizontally).isTrue() + assertThat(result.cropRect.left).isWithin(0.0001f).of(0.4f) + assertThat(result.cropRect.right).isWithin(0.0001f).of(0.9f) + assertThat(result.cropRect.top).isWithin(0.0001f).of(0.3f) + assertThat(result.cropRect.bottom).isWithin(0.0001f).of(0.9f) + assertThat(result.hasChanges).isTrue() + } + + @Test + fun `flip vertical twice resets to default state`() { + val edits = AttachmentImageEdits().flipVertically().flipVertically() + assertThat(edits.isFlippedVertically).isFalse() + assertThat(edits.hasChanges).isFalse() + } + + @Test + fun `flip horizontally twice resets to default state`() { + val edits = AttachmentImageEdits().flipHorizontally().flipHorizontally() + assertThat(edits.isFlippedVertically).isFalse() + assertThat(edits.hasChanges).isFalse() + } } From 959fd008ea0918d84cc9b1c8c4ebb169e7ae4846 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 3 Jun 2026 09:27:24 +0000 Subject: [PATCH 324/471] Update screenshots --- ...nts.preview.imageeditor_AttachmentImageEditorView_0_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_1_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_2_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_3_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_4_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_5_en.png | 4 ++-- ...nts.preview.imageeditor_AttachmentImageEditorView_6_en.png | 3 +++ ...nts.preview.imageeditor_AttachmentImageEditorView_7_en.png | 3 +++ ...s.impl.attachments.preview_AttachmentsPreviewView_6_en.png | 4 ++-- 9 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png index 3bd0c2ba54..148289d4dd 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51f0b3f7e4bb16728f21055de37b7b2780fd2a1fc65b6bd4564334daeab20763 -size 329042 +oid sha256:b64ae2fd2462ec3b62750084f47ec29c984ef4279a465b926c9d36e23834092e +size 328321 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png index bf0a11f093..21d03570b7 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5febc6580e4f0bda75a27445157ace7f1acb620c17cba55ca5d2a9330743c1de -size 283397 +oid sha256:864883e9220a4d653d2d6b555ace89c77891a1ade04cd2f49d89c8bb765c6774 +size 282733 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png index 2ae328d7eb..8edee63bc1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34b6dfe4e65612615c3dc87e5f65bd0b160d97527c4a4749b496bf8d48819d96 -size 256641 +oid sha256:de82970da241172b94f822f8792e6fc721dcfc7f0f67e3523962e253e9750884 +size 255933 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png index 553c54a63d..1857dc249d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d99c470fd5134e0a84b284ed32f4c93de01561e630ef32d7c22a4b476bb871b1 -size 277852 +oid sha256:e95d7091485a2e5bb3ca8966c8fa93f21e2e5fd2d939e7582224ebf32c05b619 +size 277160 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png index 6c3a6f4bf6..25a72fa80f 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b15a04a861812e3e4dee0a6a30c6afef4ab171c5261d1e5bd5a234bb7296d97 -size 251908 +oid sha256:1ce3085cf45a94e014c9ea79a46d47741fb098853ac81e7531fdbfe32e8d6f5a +size 251213 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png index 530c5e5bb6..f899ad5d43 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c32b4744750b9f18612bded2e292dde151e2bfdd8a69a36c88044f5ca3a76f8e -size 311315 +oid sha256:1ca032575e4ebd75a2ebc5a4cb9f966e48cdfa56f4ff42d6d9309fa3577a1adb +size 310609 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en.png new file mode 100644 index 0000000000..374a59ce84 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:846467c023fb2f6a091c6d350927c2891ddcd164dace189d157a8570339e2d92 +size 282932 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en.png new file mode 100644 index 0000000000..b7cd1d9387 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91472488cf265e9ba62a8b6690fa34ced41ebbf7a4668424c02464086aaf9f9c +size 283181 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png index fbff934ed8..ea4995ba89 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f52a267ee2aa300185191aa3e610787c58d7222bc8e4a7570879d4c5fd37133d -size 328936 +oid sha256:0c6a5c845db0d9ffef0074062cea67134bdabbe91fd1826b2236a0f92e4a6dff +size 328222 From 1863b3aef777ab60842204b0880d448cb02bd5c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:41:54 +0200 Subject: [PATCH 325/471] Update dependency org.matrix.rustcomponents:sdk-android to v26.06.3 (#6947) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 52f1d08862..1af9ef2e2f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -183,7 +183,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.05.26" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:26.06.3" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } From 4812cf9070f05ff3304cb15725efde27819c3915 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:18:43 +0200 Subject: [PATCH 326/471] Update dependency com.github.matrix-org:matrix-analytics-events to v0.36.0 (#6939) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1af9ef2e2f..9657aaf15d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -229,7 +229,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" posthog = "com.posthog:posthog-android:3.47.0" sentry = "io.sentry:sentry-android:8.41.0" # main branch can be tested replacing the version with main-SNAPSHOT -matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.33.2" +matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.36.0" # Emojibase matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.5.3" From 2ab780576aed9ae63dc344b9e048fad22444ef4f Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 3 Jun 2026 16:34:06 +0200 Subject: [PATCH 327/471] Remove the `FloatingDateBadge` feature flag (#6950) * Remove the `FloatingDateBadge` feature. This is now always enabled. * Update screenshots --------- Co-authored-by: ElementBot --- .../features/messages/impl/timeline/TimelinePresenter.kt | 4 ---- .../features/messages/impl/timeline/TimelineState.kt | 1 - .../messages/impl/timeline/TimelineStateProvider.kt | 2 -- .../features/messages/impl/timeline/TimelineView.kt | 2 +- .../android/libraries/featureflag/api/FeatureFlags.kt | 7 ------- ...atures.messages.impl.timeline_TimelineView_Day_9_en.png | 4 ++-- ...ures.messages.impl.timeline_TimelineView_Night_9_en.png | 4 ++-- 7 files changed, 5 insertions(+), 19 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0681a0ff38..0941b5c3f1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -148,9 +148,6 @@ class TimelinePresenter( val displayThreadSummaries by produceState(false) { value = featureFlagService.isFeatureEnabled(FeatureFlags.Threads) } - val displayFloatingDateBadge by produceState(false) { - value = featureFlagService.isFeatureEnabled(FeatureFlags.FloatingDateBadge) - } fun handleEvent(event: TimelineEvent) { when (event) { @@ -323,7 +320,6 @@ class TimelinePresenter( messageShieldDialogData = messageShieldDialogData.value, resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, displayThreadSummaries = displayThreadSummaries, - displayFloatingDateBadge = displayFloatingDateBadge, eventSink = ::handleEvent, ) } 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 1869ad6906..03f0083856 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 @@ -34,7 +34,6 @@ data class TimelineState( val messageShieldDialogData: MessageShieldData?, val resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState, val displayThreadSummaries: Boolean, - val displayFloatingDateBadge: Boolean, val eventSink: (TimelineEvent) -> Unit, ) { private val lastTimelineEvent = timelineItems.firstOrNull { it is TimelineItem.Event } as? TimelineItem.Event diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 0bf293eca4..3ed50f34a0 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 @@ -58,7 +58,6 @@ fun aTimelineState( messageShield: MessageShield? = null, resolveVerifiedUserSendFailureState: ResolveVerifiedUserSendFailureState = aResolveVerifiedUserSendFailureState(), displayThreadSummaries: Boolean = false, - displayFloatingDateBadge: Boolean = false, eventSink: (TimelineEvent) -> Unit = {}, ): TimelineState { val focusedEventId = timelineItems.filterIsInstance().getOrNull(focusedEventIndex)?.eventId @@ -78,7 +77,6 @@ fun aTimelineState( messageShieldDialogData = messageShield?.let { MessageShieldData(it) }, resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, displayThreadSummaries = displayThreadSummaries, - displayFloatingDateBadge = displayFloatingDateBadge, eventSink = eventSink, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 41a828abb4..0791366105 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -213,7 +213,7 @@ fun TimelineView( onFocusEventRender = ::onFocusEventRender, ) - if (state.displayFloatingDateBadge && useReverseLayout) { + if (useReverseLayout) { FloatingDateBadgeOverlay( lazyListState = lazyListState, timelineItems = state.timelineItems, 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 097df99800..ec6aaba3d3 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 @@ -108,13 +108,6 @@ enum class FeatureFlags( defaultValue = { true }, isFinished = false, ), - FloatingDateBadge( - key = "feature.floating_date_badge", - title = "Display sticky date headers in the timeline", - description = "When scrolling, a sticky date badge will be displayed so you can easily know on which date the messages you're seeing were sent.", - defaultValue = { false }, - isFinished = false, - ), SlashCommand( key = "feature.slash_command", title = "Parse slash commands in the message composer", 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 6ff4840c57..7e8f621890 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:fbd5652457d495c3c6692c151be27c985cf47e95c8b89af9b6edc8c1b734acc8 -size 371265 +oid sha256:f90a31ccde2880e01393be2bd36e9554d8087aa52bfe36e29b8514a32fea3dcd +size 752143 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 825618ae17..180c910e51 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:75ace43e27a0c8f9786b0494931c3ef5e12d903e7417dddecfb34c9526e74f3c -size 149314 +oid sha256:a853885aac641afe3fb28f3a1d9ebf6744862f153ea22bc2d511ff957b814a19 +size 747294 From 63ba6d2b18418b2f6ceda7432205cf34d95df670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 3 Jun 2026 16:36:52 +0200 Subject: [PATCH 328/471] Try fixing flaky screenshots for location timeline items Replace the `rememberAsyncImagePainter` call when in preview mode with fake values --- .../features/location/api/StaticMapView.kt | 67 ++++++++++++------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index a61cbe1c24..225032ce6e 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -8,6 +8,7 @@ package io.element.android.features.location.api +import android.graphics.Bitmap import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints @@ -24,13 +25,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.core.graphics.createBitmap import coil3.Extras +import coil3.asImage import coil3.compose.AsyncImagePainter import coil3.compose.rememberAsyncImagePainter import coil3.request.ImageRequest +import coil3.request.SuccessResult import io.element.android.compound.theme.ElementTheme import io.element.android.features.location.api.internal.StaticMapPlaceholder import io.element.android.features.location.api.internal.StaticMapUrlBuilder @@ -128,32 +133,46 @@ private fun BoxWithConstraintsScope.LoadableMapContent( var retryHash by remember { mutableIntStateOf(0) } val builder = remember { StaticMapUrlBuilder() } - val painter = rememberAsyncImagePainter( - model = if (constraints.isZero) { - // Avoid building a URL if any of the size constraints is zero - null - } else { - ImageRequest.Builder(context) - .data( - builder.build( - lat = location.lat, - lon = location.lon, - zoom = zoom, - darkMode = darkMode, - width = constraints.maxWidth, - height = constraints.maxHeight, - density = LocalDensity.current.density, + val (painter, state) = if (LocalInspectionMode.current) { + val painter = painterResource(R.drawable.blurred_map) + val state = AsyncImagePainter.State.Success( + painter = painter, + result = SuccessResult( + image = createBitmap(1, 1, Bitmap.Config.ALPHA_8).asImage(), + request = ImageRequest.Builder(context).build() + ) + ) + painter to state + } else { + val painter = rememberAsyncImagePainter( + model = if (constraints.isZero) { + // Avoid building a URL if any of the size constraints is zero + null + } else { + ImageRequest.Builder(context) + .data( + builder.build( + lat = location.lat, + lon = location.lon, + zoom = zoom, + darkMode = darkMode, + width = constraints.maxWidth, + height = constraints.maxHeight, + density = LocalDensity.current.density, + ) ) - ) - .size(width = constraints.maxWidth, height = constraints.maxHeight) - .apply { - extras.set(Extras.Key("retry_hash"), retryHash).build() - } - .build() - } - ) + .size(width = constraints.maxWidth, height = constraints.maxHeight) + .apply { + extras.set(Extras.Key("retry_hash"), retryHash).build() + } + .build() + } + ) + + val state by painter.state.collectAsState() + painter to state + } - val state by painter.state.collectAsState() when (state) { is AsyncImagePainter.State.Success -> { Image( From efbe5e43f4a902466fcb1ed0bf5ad2492b6add50 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 3 Jun 2026 14:52:45 +0000 Subject: [PATCH 329/471] Update screenshots --- .../images/features.location.api_StaticMapView_Day_0_en.png | 4 ++-- .../images/features.location.api_StaticMapView_Night_0_en.png | 4 ++-- ...ine.components.event_TimelineItemLocationView_Day_0_en.png | 4 ++-- ...ine.components.event_TimelineItemLocationView_Day_1_en.png | 4 ++-- ...ine.components.event_TimelineItemLocationView_Day_3_en.png | 4 ++-- ...e.components.event_TimelineItemLocationView_Night_0_en.png | 4 ++-- ...e.components.event_TimelineItemLocationView_Night_1_en.png | 4 ++-- ...e.components.event_TimelineItemLocationView_Night_3_en.png | 4 ++-- .../features.messages.impl.timeline_TimelineView_Day_9_en.png | 4 ++-- ...eatures.messages.impl.timeline_TimelineView_Night_9_en.png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Day_0_en.png index cedf0b1b7c..08d12dee1d 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56a86695d2c25c94a8e79c27c8f525229cc348201073566909f83a681b37ac30 -size 251329 +oid sha256:96a7cf99e31fcf4264665d5947263819d9fdb0df36500a5e5f670029813e6ea9 +size 169908 diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Night_0_en.png index dea0d3f21c..ee1b34be27 100644 --- a/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.location.api_StaticMapView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c2b2a7071bd1c21ff706525e2416169507fef364485c651c429baefa15d4c9f -size 104240 +oid sha256:fdb8317be088145c6ac24551a2c330e24f8396744e491f9daecea5144f4fbc66 +size 65342 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en.png index c11e6b978e..ed6e6824ff 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3daba890afd2533e1746f1ffd0c535674a4a238e4591da2a6bcbe31716d26858 -size 143676 +oid sha256:2c6a096640356f79c0812ef1e75f7e34f2b1cd5c61ea1f1663139192de0d9e5b +size 133923 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png index def7635efc..649c8be5bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9f2f0e8ba6829cb3d83dcc0c6d3ed5a91a0346771f84adb6731ba5fdac01f4d -size 122009 +oid sha256:85bf71d3c04e2992e7557074a119664d4a8608092123a5b8e3acfb06ada6c356 +size 114920 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png index 07613eb447..69f33275b8 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f85b9e88dca466b567769291fb69afac535135c3a07f412d6b8203968460519 -size 120940 +oid sha256:9b7bf2e64df1e9ddd57052374be6d6f912a982d51106852b50952d0ac367a5f6 +size 114157 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en.png index b9280ec225..103bc9cde5 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:463a2e544b2806f4082c813ec3ba8c6ce0ad0ebcd8ee32666806757a90c248f5 -size 57131 +oid sha256:2a0493e053d6e49bf8c80f40efb056ab433432cb2abb876d295d1cb7977fdc66 +size 58552 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png index f06f86068a..41e2855f01 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00da192b3b46897c9d50986ecd84e07332cd2b490ebc8f3893a9497e72f18af8 -size 41478 +oid sha256:4b80f58cc648745a64aa6163e1c6d9b7e4c578abab3ba11734228b63b922b95b +size 43823 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png index 4cb5fc6e48..4c0e098731 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c015bae5b3ce8c4447c62a13fb74d855c23b0e41bf0b128a1834d30781c0d1f8 -size 41110 +oid sha256:aaada944746ed97d74bbd358e071d46ba18696fb9ad19be41647159c075a51dd +size 43093 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 7e8f621890..7242fa9889 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:f90a31ccde2880e01393be2bd36e9554d8087aa52bfe36e29b8514a32fea3dcd -size 752143 +oid sha256:9e212eb68de26ca2765e2a67043b2e3bbb3c763884684a89cbba8792f8cb1190 +size 369666 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 180c910e51..f9e7cff264 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:a853885aac641afe3fb28f3a1d9ebf6744862f153ea22bc2d511ff957b814a19 -size 747294 +oid sha256:5db05495db43f14234678b60efe04e7cad6d5d54fdd1e8f9550e0a02b02369bb +size 154516 From 27cdc0fe7dad1f3070eee2cb8d71184de3853f2e Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:08:35 +0300 Subject: [PATCH 330/471] Add unread count to the room unread indicator (#6887) * Add unread indicator count * Address review --- .../impl/components/RoomListContentView.kt | 1 + .../home/impl/components/RoomSummaryRow.kt | 14 ++++++++- .../roomlist/RoomListContentStateProvider.kt | 2 ++ .../home/impl/roomlist/RoomListPresenter.kt | 13 ++++++-- .../home/impl/roomlist/RoomListState.kt | 1 + .../impl/roomlist/RoomListPresenterTest.kt | 4 +++ .../designsystem/atomic/atoms/CounterAtom.kt | 10 +++++-- .../atomic/atoms/UnreadIndicatorAtom.kt | 30 ++++++++++++++----- .../libraries/featureflag/api/FeatureFlags.kt | 7 +++++ 9 files changed, 69 insertions(+), 13 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt index dee175a7b1..266b6f5a59 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt @@ -279,6 +279,7 @@ private fun RoomsViewList( hideInviteAvatars = hideInvitesAvatars, isInviteSeen = room.displayType == RoomSummaryDisplayType.INVITE && state.seenRoomInvites.contains(room.roomId), + showUnreadCount = state.showUnreadCount, onClick = onRoomClick, eventSink = eventSink, ) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index 72ecb5046d..934f60aa29 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -75,6 +75,7 @@ internal fun RoomSummaryRow( room: RoomListRoomSummary, hideInviteAvatars: Boolean, isInviteSeen: Boolean, + showUnreadCount: Boolean = false, onClick: (RoomListRoomSummary) -> Unit, eventSink: (RoomListEvent) -> Unit, modifier: Modifier = Modifier, @@ -127,7 +128,7 @@ internal fun RoomSummaryRow( timestamp = room.timestamp, isHighlighted = room.isHighlighted ) - MessagePreviewAndIndicatorRow(room = room) + MessagePreviewAndIndicatorRow(room = room, showUnreadCount = showUnreadCount) } } RoomSummaryDisplayType.KNOCKED -> { @@ -273,6 +274,7 @@ private fun InviteSubtitle( @Composable private fun MessagePreviewAndIndicatorRow( room: RoomListRoomSummary, + showUnreadCount: Boolean, modifier: Modifier = Modifier, ) { Row( @@ -360,8 +362,18 @@ private fun MessagePreviewAndIndicatorRow( } if (room.hasNewContent) { val contentDescription = stringResource(CommonStrings.a11y_notifications_new_messages) + val count = if (showUnreadCount) { + if (room.userDefinedNotificationMode == RoomNotificationMode.MUTE) { + room.numberOfUnreadMessages + } else { + room.numberOfUnreadNotifications + } + } else { + null + } UnreadIndicatorAtom( color = tint, + count = count, contentDescription = contentDescription, ) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt index c79e79be73..0ac2b7bf62 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListContentStateProvider.kt @@ -36,6 +36,7 @@ open class RoomListContentStateProvider : PreviewParameterProvider = aRoomListRoomSummaryList(), fullScreenIntentPermissionsState: FullScreenIntentPermissionsState = aFullScreenIntentPermissionsState(), batteryOptimizationState: BatteryOptimizationState = aBatteryOptimizationState(), @@ -43,6 +44,7 @@ internal fun aRoomsContentState( ) = RoomListContentState.Rooms( securityBannerState = securityBannerState, showNewNotificationSoundBanner = showNewNotificationSoundBanner, + showUnreadCount = showUnreadCount, fullScreenIntentPermissionsState = fullScreenIntentPermissionsState, batteryOptimizationState = batteryOptimizationState, summaries = summaries, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt index 2010555cd7..a741da14e9 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenter.kt @@ -44,6 +44,8 @@ import io.element.android.features.leaveroom.api.LeaveRoomEvent import io.element.android.features.leaveroom.api.LeaveRoomState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId @@ -90,6 +92,7 @@ class RoomListPresenter( private val announcementService: AnnouncementService, private val coldStartWatcher: AnalyticsColdStartWatcher, private val spaceFiltersPresenter: Presenter, + private val featureFlagService: FeatureFlagService, ) : Presenter { private val encryptionService = client.encryptionService @@ -165,13 +168,17 @@ class RoomListPresenter( roomListDataSource.updateFilter(allFilters) } + val canReportRoom by produceState(false) { value = client.canReportRoom() } + val showUnreadCount by produceState(false) { + value = featureFlagService.isFeatureEnabled(FeatureFlags.UnreadIndicatorCount) + } + val contentState = roomListContentState( securityBannerDismissed, showNewNotificationSoundBanner, + showUnreadCount, ) - val canReportRoom by produceState(false) { value = client.canReportRoom() } - return RoomListState( contextMenu = contextMenu.value, declineInviteMenu = declineInviteMenu.value, @@ -226,6 +233,7 @@ class RoomListPresenter( private fun roomListContentState( securityBannerDismissed: Boolean, showNewNotificationSoundBanner: Boolean, + showUnreadCount: Boolean, ): RoomListContentState { val roomSummaries by produceState(initialValue = AsyncData.Loading()) { roomListDataSource.roomSummariesFlow.collect { value = AsyncData.Success(it) } @@ -254,6 +262,7 @@ class RoomListPresenter( RoomListContentState.Rooms( securityBannerState = securityBannerState, showNewNotificationSoundBanner = showNewNotificationSoundBanner, + showUnreadCount = showUnreadCount, fullScreenIntentPermissionsState = fullScreenIntentPermissionsPresenter.present(), batteryOptimizationState = batteryOptimizationPresenter.present(), summaries = roomSummaries.dataOrNull().orEmpty().toImmutableList(), diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt index e0f4943621..c03bd56664 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/roomlist/RoomListState.kt @@ -72,6 +72,7 @@ sealed interface RoomListContentState { val fullScreenIntentPermissionsState: FullScreenIntentPermissionsState, val batteryOptimizationState: BatteryOptimizationState, val showNewNotificationSoundBanner: Boolean, + val showUnreadCount: Boolean, val summaries: ImmutableList, val seenRoomInvites: ImmutableSet, ) : RoomListContentState diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt index 5c2c3e294b..2f3f1c87a5 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListPresenterTest.kt @@ -36,6 +36,8 @@ import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.eventformatter.api.RoomLatestEventFormatter import io.element.android.libraries.eventformatter.test.FakeRoomLatestEventFormatter +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.fullscreenintent.api.aFullScreenIntentPermissionsState import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId @@ -669,6 +671,7 @@ class RoomListPresenterTest { appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), announcementService: AnnouncementService = FakeAnnouncementService(), + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), ) = RoomListPresenter( client = client, leaveRoomPresenter = { leaveRoomState }, @@ -697,5 +700,6 @@ class RoomListPresenterTest { seenInvitesStore = seenInvitesStore, announcementService = announcementService, coldStartWatcher = FakeAnalyticsColdStartWatcher(), + featureFlagService = featureFlagService, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt index a051fd4c80..3bbd3c12a7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.unit.dp @@ -37,13 +38,18 @@ private const val MAX_COUNT_STRING = "+$MAX_COUNT" * @param count The number to display. If the number is greater than [MAX_COUNT], the counter will display [MAX_COUNT_STRING]. * If the number is less than 1, the counter will not be displayed. * @param modifier The modifier to apply to this layout. + * @param containerColor The background color of the counter. When null, uses [isCritical] to pick a default. + * @param contentColor The text color inside the counter. When null, uses [textOnSolidPrimary]. * @param textStyle The style to apply to the text inside the counter. * @param isCritical If true, the counter will use a critical color scheme, otherwise it will use an accent color scheme. + * Only used when [containerColor] is null. */ @Composable fun CounterAtom( count: Int, modifier: Modifier = Modifier, + containerColor: Color? = null, + contentColor: Color? = null, textStyle: TextStyle = CounterAtomDefaults.textStyle, isCritical: Boolean = false, ) { @@ -65,7 +71,7 @@ fun CounterAtom( .size(squareSize.toDp() + 1.dp) .clip(CircleShape) .background( - if (isCritical) { + containerColor ?: if (isCritical) { ElementTheme.colors.iconCriticalPrimary } else { ElementTheme.colors.iconAccentPrimary @@ -76,7 +82,7 @@ fun CounterAtom( modifier = Modifier.align(Alignment.Center), text = countAsText, style = textStyle, - color = ElementTheme.colors.textOnSolidPrimary, + color = contentColor ?: ElementTheme.colors.textOnSolidPrimary, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt index d2db3aec8e..ff49bab06f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.designsystem.atomic.atoms import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable @@ -29,19 +30,32 @@ import io.element.android.libraries.designsystem.theme.unreadIndicator fun UnreadIndicatorAtom( modifier: Modifier = Modifier, size: Dp = 12.dp, + count: Long? = null, color: Color = ElementTheme.colors.unreadIndicator, isVisible: Boolean = true, contentDescription: String? = null, ) { - Box( - modifier = modifier - .semantics { + when { + !isVisible -> Spacer(modifier = modifier.size(size)) + count != null && count >= 1 -> CounterAtom( + count = count.toInt(), + modifier = modifier.semantics { contentDescription?.let { this.contentDescription = it } - } - .size(size) - .clip(CircleShape) - .background(if (isVisible) color else Color.Transparent) - ) + }, + containerColor = color, + contentColor = ElementTheme.colors.bgCanvasDefault, + textStyle = ElementTheme.typography.fontBodySmMedium, + ) + else -> Box( + modifier = modifier + .semantics { + contentDescription?.let { this.contentDescription = it } + } + .size(size) + .clip(CircleShape) + .background(color), + ) + } } @PreviewsDayNight 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 ec6aaba3d3..0127e41c99 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 @@ -130,4 +130,11 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), + UnreadIndicatorCount( + key = "feature.unread_indicator_count", + title = "Unread indicator count", + description = "Show the number of unread messages on the unread indicator in the room list.", + defaultValue = { false }, + isFinished = false, + ), } From 930aa4f1db869f147806a3cd43bb9461b6ae845f Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 3 Jun 2026 21:52:54 +0200 Subject: [PATCH 331/471] change: ensure UserLocationState is computed from the Presenters instead of Views --- .../location/impl/common/UserLocationState.kt | 42 --------- .../impl/common/ui/UserLocationPuck.kt | 54 ++++-------- .../DefaultUserLocationStateFactory.kt | 38 +++++++++ .../impl/common/userlocation/Location.kt | 26 ++++++ .../PlatformLocationProvider.kt | 2 +- .../common/userlocation/UserLocationState.kt | 18 ++++ .../UserLocationTrackingEffect.kt} | 36 ++++++-- .../service/LiveLocationSharingService.kt | 2 +- .../impl/share/ShareLocationPresenter.kt | 6 +- .../location/impl/share/ShareLocationState.kt | 3 +- .../impl/share/ShareLocationStateProvider.kt | 14 +-- .../location/impl/share/ShareLocationView.kt | 16 ++-- .../impl/show/ShowLocationPresenter.kt | 85 ++++++++++--------- .../location/impl/show/ShowLocationState.kt | 4 +- .../impl/show/ShowLocationStateProvider.kt | 20 ++--- .../location/impl/show/ShowLocationView.kt | 15 ++-- .../common/FakeUserLocationStateFactory.kt | 18 ++++ .../DefaultShareLocationEntryPointTest.kt | 2 + .../impl/share/ShareLocationPresenterTest.kt | 7 +- .../impl/share/ShareLocationViewTest.kt | 2 - .../show/DefaultShowLocationEntryPointTest.kt | 2 + .../impl/show/ShowLocationPresenterTest.kt | 81 ++++++------------ 22 files changed, 265 insertions(+), 228 deletions(-) delete mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/DefaultUserLocationStateFactory.kt create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/Location.kt rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/{ => userlocation}/PlatformLocationProvider.kt (98%) create mode 100644 features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationState.kt rename features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/{ui/SimpleLocationTrackingEffect.kt => userlocation/UserLocationTrackingEffect.kt} (60%) create mode 100644 features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/FakeUserLocationStateFactory.kt diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt deleted file mode 100644 index 01533a421d..0000000000 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/UserLocationState.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.location.impl.common - -import android.annotation.SuppressLint -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalInspectionMode -import org.maplibre.compose.location.DesiredAccuracy -import org.maplibre.compose.location.Location -import org.maplibre.compose.location.rememberNullLocationProvider -import org.maplibre.spatialk.units.extensions.meters -import kotlin.time.Duration.Companion.seconds - -class UserLocationState(locationState: State) { - val location: Location? by locationState -} - -@SuppressLint("MissingPermission") -@Composable -fun rememberUserLocationState(hasLocationPermission: Boolean): UserLocationState { - val isPreview = LocalInspectionMode.current - val locationProvider = if (isPreview || !hasLocationPermission) { - rememberNullLocationProvider() - } else { - rememberPlatformLocationProvider( - updateInterval = 5.seconds, - desiredAccuracy = DesiredAccuracy.High, - minDistance = 5.meters, - ) - } - val locationState = locationProvider.location.collectAsState() - return remember { UserLocationState(locationState) } -} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt index 5bcbf6a9b2..074404a4cc 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/UserLocationPuck.kt @@ -10,9 +10,8 @@ package io.element.android.features.location.impl.common.ui import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.features.location.impl.common.MapDefaults -import io.element.android.features.location.impl.common.UserLocationState import org.maplibre.compose.camera.CameraState +import org.maplibre.compose.location.Location import org.maplibre.compose.location.LocationPuck import org.maplibre.compose.location.LocationPuckColors import org.maplibre.compose.location.LocationPuckSizes @@ -20,40 +19,23 @@ import org.maplibre.compose.location.LocationPuckSizes @Composable fun UserLocationPuck( cameraState: CameraState, - locationState: UserLocationState, - trackUserLocation: Boolean, + location: Location?, ) { - SimpleLocationTrackingEffect( - locationState = locationState, - enabled = trackUserLocation, - ) { currentLocation -> - val target = currentLocation?.position?.value ?: cameraState.position.target - val newPosition = cameraState.position.copy( - target = target, - // Force pointing to NORTH - bearing = 0.0, - zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) + LocationPuck( + idPrefix = "user-location", + location = location, + cameraState = cameraState, + accuracyThreshold = Float.POSITIVE_INFINITY, + showBearingAccuracy = false, + showBearing = false, + sizes = LocationPuckSizes( + dotRadius = 8.dp, + dotStrokeWidth = 2.dp, + ), + colors = LocationPuckColors( + dotFillColorCurrentLocation = ElementTheme.colors.iconAccentPrimary, + dotFillColorOldLocation = ElementTheme.colors.iconAccentTertiary, + dotStrokeColor = ElementTheme.colors.bgCanvasDefault, ) - cameraState.animateTo(newPosition) - } - val location = locationState.location - if (location != null) { - LocationPuck( - idPrefix = "user-location", - location = location, - cameraState = cameraState, - accuracyThreshold = Float.POSITIVE_INFINITY, - showBearingAccuracy = false, - showBearing = false, - sizes = LocationPuckSizes( - dotRadius = 8.dp, - dotStrokeWidth = 2.dp, - ), - colors = LocationPuckColors( - dotFillColorCurrentLocation = ElementTheme.colors.iconAccentPrimary, - dotFillColorOldLocation = ElementTheme.colors.iconAccentTertiary, - dotStrokeColor = ElementTheme.colors.bgCanvasDefault, - ) - ) - } + ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/DefaultUserLocationStateFactory.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/DefaultUserLocationStateFactory.kt new file mode 100644 index 0000000000..01a2b92d4a --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/DefaultUserLocationStateFactory.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common.userlocation + +import android.annotation.SuppressLint +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.di.RoomScope +import org.maplibre.compose.location.DesiredAccuracy +import org.maplibre.compose.location.rememberNullLocationProvider +import org.maplibre.spatialk.units.extensions.meters +import kotlin.time.Duration.Companion.seconds + +@ContributesBinding(RoomScope::class) +class DefaultUserLocationStateFactory : UserLocationState.Factory { + @Composable + override fun create(hasLocationPermission: Boolean): UserLocationState { + val locationProvider = if (!hasLocationPermission) { + rememberNullLocationProvider() + } else { + @SuppressLint("MissingPermission") + rememberPlatformLocationProvider( + updateInterval = 5.seconds, + desiredAccuracy = DesiredAccuracy.High, + minDistance = 5.meters, + ) + } + val location by locationProvider.location.collectAsState() + return UserLocationState(location) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/Location.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/Location.kt new file mode 100644 index 0000000000..9777e14af5 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/Location.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common.userlocation + +import io.element.android.features.location.api.Location +import org.maplibre.compose.location.PositionWithAccuracy +import org.maplibre.spatialk.geojson.Position +import org.maplibre.spatialk.units.extensions.meters +import kotlin.time.TimeSource +import org.maplibre.compose.location.Location as MapLibreLocation + +fun Location.asMapLibreLocation(): MapLibreLocation { + return MapLibreLocation( + position = PositionWithAccuracy( + value = Position(latitude = lat, longitude = lon), + accuracy = accuracy?.toDouble()?.meters + ), + // Not relevant as not used + timestamp = TimeSource.Monotonic.markNow(), + ) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/PlatformLocationProvider.kt similarity index 98% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/PlatformLocationProvider.kt index 7988c73e68..54e6d19cf6 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/PlatformLocationProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/PlatformLocationProvider.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.location.impl.common +package io.element.android.features.location.impl.common.userlocation import android.Manifest import android.annotation.SuppressLint diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationState.kt new file mode 100644 index 0000000000..d9dc2037a6 --- /dev/null +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationState.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common.userlocation + +import androidx.compose.runtime.Composable +import org.maplibre.compose.location.Location + +data class UserLocationState(val location: Location?) { + fun interface Factory { + @Composable + fun create(hasLocationPermission: Boolean): UserLocationState + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationTrackingEffect.kt similarity index 60% rename from features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt rename to features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationTrackingEffect.kt index cda360be9e..ca6c10b7fa 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/ui/SimpleLocationTrackingEffect.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/common/userlocation/UserLocationTrackingEffect.kt @@ -5,15 +5,16 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.location.impl.common.ui +package io.element.android.features.location.impl.common.userlocation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow -import io.element.android.features.location.impl.common.UserLocationState +import io.element.android.features.location.impl.common.MapDefaults import kotlinx.coroutines.flow.distinctUntilChanged +import org.maplibre.compose.camera.CameraState import org.maplibre.compose.location.Location import kotlin.math.abs @@ -22,16 +23,17 @@ import kotlin.math.abs * TODO remove once https://github.com/maplibre/maplibre-compose/issues/808 is fixed */ @Composable -internal fun SimpleLocationTrackingEffect( +internal fun UserLocationTrackingEffect( locationState: UserLocationState, enabled: Boolean = true, precision: Double = 0.00001, onLocationChange: suspend (Location?) -> Unit, ) { val latestOnLocationChange by rememberUpdatedState(onLocationChange) - LaunchedEffect(locationState, enabled) { + val latestLocationState by rememberUpdatedState(locationState) + LaunchedEffect(enabled) { if (!enabled) return@LaunchedEffect - val locationStateFlow = snapshotFlow { locationState.location } + val locationStateFlow = snapshotFlow { latestLocationState.location } locationStateFlow .distinctUntilChanged { oldLocation, newLocation -> if (oldLocation != null && newLocation != null) { @@ -49,3 +51,27 @@ internal fun SimpleLocationTrackingEffect( } } } + +@Composable +internal fun UserLocationTrackingEffect( + cameraState: CameraState, + locationState: UserLocationState, + enabled: Boolean = true, + precision: Double = 0.00001, +) { + UserLocationTrackingEffect( + locationState = locationState, + enabled = enabled, + precision = precision + ) { location -> + val target = location?.position?.value ?: cameraState.position.target + cameraState.animateTo( + cameraState.position.copy( + target = target, + // Force pointing to NORTH + bearing = 0.0, + zoom = cameraState.position.zoom.coerceAtLeast(MapDefaults.DEFAULT_ZOOM) + ) + ) + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt index d682ba0df6..21a0a081d8 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/live/service/LiveLocationSharingService.kt @@ -14,7 +14,7 @@ import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION import android.os.IBinder import androidx.core.app.ServiceCompat import dev.zacsweers.metro.Inject -import io.element.android.features.location.impl.common.PlatformLocationProvider +import io.element.android.features.location.impl.common.userlocation.PlatformLocationProvider import io.element.android.features.location.impl.di.LocationBindings import io.element.android.features.location.impl.live.notification.LiveLocationSharingNotificationCreator import io.element.android.libraries.architecture.bindings diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt index 6a769f8958..5b0d7679f7 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenter.kt @@ -33,6 +33,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsP import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.sendLiveLocationPermissions import io.element.android.features.location.impl.common.toDialogState +import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.features.location.impl.live.LiveLocationStore import io.element.android.features.messages.api.MessageComposerContext import io.element.android.libraries.architecture.AsyncAction @@ -70,6 +71,7 @@ class ShareLocationPresenter( private val durationFormatter: DurationFormatter, private val liveLocationShareManager: ActiveLiveLocationShareManager, private val liveLocationStore: LiveLocationStore, + private val userLocationStateFactory: UserLocationState.Factory, ) : Presenter { @AssistedFactory fun interface Factory { @@ -123,6 +125,8 @@ class ShareLocationPresenter( } } + val userLocationState = userLocationStateFactory.create(permissionsState.isAnyGranted) + LaunchedEffect(permissionsState.permissions) { checkLocationConstraints() } fun handleEvent(event: ShareLocationEvent) { @@ -171,7 +175,7 @@ class ShareLocationPresenter( currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, - hasLocationPermission = permissionsState.isAnyGranted, + userLocationState = userLocationState, canShareLiveLocation = timelineMode.canShareLiveLocation(), appName = appName, startLiveLocationAction = startLiveLocationAction.value, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt index 9e78345bab..fb1b803435 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationState.kt @@ -9,6 +9,7 @@ package io.element.android.features.location.impl.share import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.user.MatrixUser @@ -19,7 +20,7 @@ data class ShareLocationState( val currentUser: MatrixUser, val dialogState: Dialog, val trackUserLocation: Boolean, - val hasLocationPermission: Boolean, + val userLocationState: UserLocationState, val appName: String, val canShareLiveLocation: Boolean, val startLiveLocationAction: AsyncAction, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt index e7942b12df..ed8d46afd3 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationStateProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.location.impl.share import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.UserId @@ -26,43 +27,35 @@ class ShareLocationStateProvider : PreviewParameterProvider aShareLocationState( dialogState = ShareLocationState.Dialog.None, trackUserPosition = false, - hasLocationPermission = false, ), aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied), trackUserPosition = false, - hasLocationPermission = false, ), aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale), trackUserPosition = false, - hasLocationPermission = false, ), aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled), trackUserPosition = false, - hasLocationPermission = true, ), aShareLocationState( dialogState = ShareLocationState.Dialog.None, trackUserPosition = false, - hasLocationPermission = true, ), aShareLocationState( dialogState = ShareLocationState.Dialog.None, trackUserPosition = true, - hasLocationPermission = true, ), aShareLocationState( dialogState = ShareLocationState.Dialog.None, trackUserPosition = true, - hasLocationPermission = true, canShareLiveLocation = true, ), aShareLocationState( dialogState = ShareLocationState.Dialog.LiveLocationDisclaimer, trackUserPosition = true, - hasLocationPermission = true, canShareLiveLocation = true, ), aShareLocationState( @@ -74,7 +67,6 @@ class ShareLocationStateProvider : PreviewParameterProvider ) ), trackUserPosition = true, - hasLocationPermission = true, canShareLiveLocation = true, ), aShareLocationState( @@ -88,7 +80,7 @@ fun aShareLocationState( currentUser: MatrixUser = MatrixUser(UserId("@user:matrix.org")), dialogState: ShareLocationState.Dialog = ShareLocationState.Dialog.None, trackUserPosition: Boolean = false, - hasLocationPermission: Boolean = false, + userLocationState: UserLocationState = UserLocationState(null), canShareLiveLocation: Boolean = false, appName: String = APP_NAME, startLiveLocationAction: AsyncAction = AsyncAction.Uninitialized, @@ -99,7 +91,7 @@ fun aShareLocationState( currentUser = currentUser, dialogState = dialogState, trackUserLocation = trackUserPosition, - hasLocationPermission = hasLocationPermission, + userLocationState = userLocationState, canShareLiveLocation = canShareLiveLocation, appName = appName, startLiveLocationAction = startLiveLocationAction, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt index da7410f0bb..4760b3597c 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/share/ShareLocationView.kt @@ -39,12 +39,11 @@ import io.element.android.features.location.api.Location import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.impl.R import io.element.android.features.location.impl.common.MapDefaults -import io.element.android.features.location.impl.common.UserLocationState -import io.element.android.features.location.impl.common.rememberUserLocationState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialog import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold import io.element.android.features.location.impl.common.ui.UserLocationPuck +import io.element.android.features.location.impl.common.userlocation.UserLocationTrackingEffect import io.element.android.features.location.impl.share.ShareLocationEvent.StartLiveLocationShare import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.LocationPin @@ -109,7 +108,6 @@ fun ShareLocationView( bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded) ) val cameraState = rememberCameraState(firstPosition = MapDefaults.defaultCameraPosition) - val userLocationState = rememberUserLocationState(state.hasLocationPermission) LaunchedEffect(cameraState.isCameraMoving) { if (cameraState.moveReason == CameraMoveReason.GESTURE) { @@ -136,15 +134,18 @@ fun ShareLocationView( BottomSheetContent( cameraState = cameraState, state = state, - userLocationState = userLocationState, navigateUp = navigateUp ) }, mapContent = { + UserLocationTrackingEffect( + cameraState = cameraState, + locationState = state.userLocationState, + enabled = state.trackUserLocation, + ) UserLocationPuck( cameraState = cameraState, - locationState = userLocationState, - trackUserLocation = state.trackUserLocation + location = state.userLocationState.location, ) }, overlayContent = { sheetPadding -> @@ -215,11 +216,10 @@ private fun StartLiveLocationActionView( private fun BottomSheetContent( cameraState: CameraState, state: ShareLocationState, - userLocationState: UserLocationState, navigateUp: () -> Unit, ) { Spacer(Modifier.height(20.dp)) - val userLocation = userLocationState.location + val userLocation = state.userLocationState.location if (state.trackUserLocation && userLocation != null) { ShareCurrentLocationItem { state.eventSink( diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index bdb6dd26b8..8ea857c6b9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -10,12 +10,14 @@ package io.element.android.features.location.impl.show import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory @@ -23,6 +25,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.api.live.ActiveLiveLocationShareManager +import io.element.android.features.location.api.live.isCurrentlySharing import io.element.android.features.location.impl.common.LocationConstraintsCheck import io.element.android.features.location.impl.common.MapDefaults import io.element.android.features.location.impl.common.SendLiveLocationPermissions @@ -33,6 +36,8 @@ import io.element.android.features.location.impl.common.permissions.PermissionsP import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.location.impl.common.toDialogState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.impl.common.userlocation.UserLocationState +import io.element.android.features.location.impl.common.userlocation.asMapLibreLocation import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.mapState @@ -52,8 +57,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -@AssistedInject -class ShowLocationPresenter( +@AssistedInject class ShowLocationPresenter( @Assisted private val mode: ShowLocationMode, permissionsPresenterFactory: PermissionsPresenter.Factory, private val locationActions: LocationActions, @@ -63,9 +67,9 @@ class ShowLocationPresenter( private val client: MatrixClient, private val joinedRoom: JoinedRoom, private val liveLocationShareManager: ActiveLiveLocationShareManager, + private val userLocationStateFactory: UserLocationState.Factory, ) : Presenter { - @AssistedFactory - fun interface Factory { + @AssistedFactory fun interface Factory { fun create(mode: ShowLocationMode): ShowLocationPresenter } @@ -155,55 +159,58 @@ class ShowLocationPresenter( val liveLocationSharesFlow = joinedRoom.subscribeToLiveLocationShares() val membersStateFlow = joinedRoom.membersStateFlow.mapState { it.joinedRoomMembers() } combine(liveLocationSharesFlow, membersStateFlow) { liveShares, members -> - liveShares - .sortedWith(comparator) - .mapNotNull { share -> - val lastLocation = share.lastLocation ?: return@mapNotNull null - val location = Location.fromGeoUri(lastLocation.geoUri) ?: return@mapNotNull null - val member = members.find { it.userId == share.userId } - val displayName = member?.getBestName() ?: share.userId.value - val avatarUrl = member?.avatarUrl - val relativeTime = dateFormatter.format(timestamp = lastLocation.timestamp, mode = DateFormatterMode.Full, useRelative = true) - val formattedTimestamp = stringProvider.getString( - CommonStrings.screen_static_location_sheet_timestamp_description, - relativeTime - ) - LocationShareItem( - userId = share.userId, - displayName = displayName, - avatarData = AvatarData( - id = share.userId.value, - name = displayName, - url = avatarUrl, - size = AvatarSize.UserListItem, - ), - formattedTimestamp = formattedTimestamp, - location = location, - isLive = true, - assetType = lastLocation.assetType, - isOwnUser = share.userId == joinedRoom.sessionId - ) - } - .toImmutableList() + liveShares.sortedWith(comparator).mapNotNull { share -> + val lastLocation = share.lastLocation ?: return@mapNotNull null + val location = Location.fromGeoUri(lastLocation.geoUri) ?: return@mapNotNull null + val member = members.find { it.userId == share.userId } + val displayName = member?.getBestName() ?: share.userId.value + val avatarUrl = member?.avatarUrl + val relativeTime = dateFormatter.format(timestamp = lastLocation.timestamp, mode = DateFormatterMode.Full, useRelative = true) + val formattedTimestamp = stringProvider.getString( + CommonStrings.screen_static_location_sheet_timestamp_description, + relativeTime + ) + LocationShareItem( + userId = share.userId, + displayName = displayName, + avatarData = AvatarData( + id = share.userId.value, + name = displayName, + url = avatarUrl, + size = AvatarSize.UserListItem, + ), + formattedTimestamp = formattedTimestamp, + location = location, + isLive = true, + assetType = lastLocation.assetType, + isOwnUser = share.userId == joinedRoom.sessionId + ) + }.toImmutableList() }.collect { value = it } }.value } } - val focusedLocation = when (mode) { - is ShowLocationMode.Static -> locationShares.firstOrNull() - is ShowLocationMode.Live -> locationShares.firstOrNull { it.userId == mode.senderId } + val updatedLocationShares by rememberUpdatedState(locationShares) + val focusedLocation by remember { + derivedStateOf { + when (mode) { + is ShowLocationMode.Static -> updatedLocationShares.firstOrNull() + is ShowLocationMode.Live -> updatedLocationShares.firstOrNull { it.userId == mode.senderId } + } + } } - + val userLocationState = userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted) return ShowLocationState( customMapStyleUrl = customMapStyleUrl, dialogState = dialogState, locationShares = locationShares, focusedLocation = focusedLocation, - hasLocationPermission = permissionsState.isAnyGranted, isTrackMyLocation = isTrackMyLocation, + userLocationState = userLocationState, isLive = mode is ShowLocationMode.Live, appName = appName, + hideUserLocationPuck = false, eventSink = ::handleEvent, ) } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt index 478a3bd730..e5ddf589b0 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationState.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState import io.element.android.features.location.impl.common.ui.LocationMarkerData +import io.element.android.features.location.impl.common.userlocation.UserLocationState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.PinVariant import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -24,9 +25,10 @@ data class ShowLocationState( val dialogState: LocationConstraintsDialogState, val locationShares: ImmutableList, val focusedLocation: LocationShareItem?, - val hasLocationPermission: Boolean, val isTrackMyLocation: Boolean, + val userLocationState: UserLocationState, val appName: String, + val hideUserLocationPuck: Boolean, val eventSink: (ShowLocationEvent) -> Unit, ) { val isSheetDraggable = isLive && locationShares.isNotEmpty() diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index 85eacd3ce4..cd7ea108bb 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.show import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.location.api.Location import io.element.android.features.location.impl.common.ui.LocationConstraintsDialogState +import io.element.android.features.location.impl.common.userlocation.UserLocationState 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 @@ -33,18 +34,9 @@ class ShowLocationStateProvider : PreviewParameterProvider { ), aShowLocationState( constraintsDialogState = LocationConstraintsDialogState.LocationServiceDisabled, - hasLocationPermission = true, - ), - aShowLocationState( - hasLocationPermission = true, - ), - aShowLocationState( - hasLocationPermission = true, - isTrackMyLocation = true, - ), - aShowLocationState( - customMapStyleUrl = AsyncData.Loading(), ), + aShowLocationState(isTrackMyLocation = true), + aShowLocationState(customMapStyleUrl = AsyncData.Loading()), ) } @@ -56,9 +48,10 @@ fun aShowLocationState( constraintsDialogState: LocationConstraintsDialogState = LocationConstraintsDialogState.None, locationShares: List = listOf(aLocationShareItem(isLive = isLive)), focusedLocation: LocationShareItem? = locationShares.firstOrNull(), - hasLocationPermission: Boolean = false, isTrackMyLocation: Boolean = false, + userLocationState: UserLocationState = UserLocationState(null), appName: String = APP_NAME, + hideUserLocationPuck: Boolean = false, eventSink: (ShowLocationEvent) -> Unit = {}, ): ShowLocationState { return ShowLocationState( @@ -66,8 +59,9 @@ fun aShowLocationState( dialogState = constraintsDialogState, locationShares = locationShares.toImmutableList(), focusedLocation = focusedLocation, - hasLocationPermission = hasLocationPermission, isTrackMyLocation = isTrackMyLocation, + userLocationState = userLocationState, + hideUserLocationPuck = hideUserLocationPuck, appName = appName, isLive = isLive, eventSink = eventSink, diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index ba60a22225..6b1b33014d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -37,13 +37,13 @@ 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.location.impl.common.MapDefaults -import io.element.android.features.location.impl.common.rememberUserLocationState import io.element.android.features.location.impl.common.ui.LocationConstraintsDialog import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton import io.element.android.features.location.impl.common.ui.LocationPinMarkers import io.element.android.features.location.impl.common.ui.LocationShareRow import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold import io.element.android.features.location.impl.common.ui.UserLocationPuck +import io.element.android.features.location.impl.common.userlocation.UserLocationTrackingEffect import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -91,7 +91,6 @@ fun ShowLocationView( } } - val userLocationState = rememberUserLocationState(state.hasLocationPermission) val scaffoldState = rememberBottomSheetScaffoldState( bottomSheetState = rememberStandardBottomSheetState(SheetValue.Expanded) ) @@ -169,11 +168,17 @@ fun ShowLocationView( } }, mapContent = { - UserLocationPuck( + UserLocationTrackingEffect( cameraState = cameraState, - locationState = userLocationState, - trackUserLocation = state.isTrackMyLocation + locationState = state.userLocationState, + enabled = state.isTrackMyLocation, ) + if (!state.hideUserLocationPuck) { + UserLocationPuck( + cameraState = cameraState, + location = state.userLocationState.location, + ) + } val markers = remember(state.locationShares) { state.locationShares.map { it.toMarkerData() }.toImmutableList() } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/FakeUserLocationStateFactory.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/FakeUserLocationStateFactory.kt new file mode 100644 index 0000000000..f6855c5667 --- /dev/null +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/common/FakeUserLocationStateFactory.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.impl.common + +import androidx.compose.runtime.Composable +import io.element.android.features.location.impl.common.userlocation.UserLocationState + +class FakeUserLocationStateFactory : UserLocationState.Factory { + @Composable + override fun create(hasLocationPermission: Boolean): UserLocationState { + return UserLocationState(null) + } +} diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt index 6f20e296d9..49df684666 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/DefaultShareLocationEntryPointTest.kt @@ -11,6 +11,7 @@ package io.element.android.features.location.impl.share import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat +import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.impl.live.LiveLocationStore @@ -56,6 +57,7 @@ class DefaultShareLocationEntryPointTest { preferenceDataStoreFactory = FakePreferenceDataStoreFactory(), sessionId = room.sessionId, ), + userLocationStateFactory = FakeUserLocationStateFactory(), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt index 5be96a50c5..f9195cd08d 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationPresenterTest.kt @@ -21,6 +21,7 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import io.element.android.features.location.api.Location import io.element.android.features.location.impl.aPermissionsState +import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsEvents @@ -102,6 +103,7 @@ class ShareLocationPresenterTest { durationFormatter = durationFormatter, liveLocationShareManager = liveLocationShareManager, liveLocationStore = liveLocationStore, + userLocationStateFactory = FakeUserLocationStateFactory(), ) @Test @@ -118,7 +120,6 @@ class ShareLocationPresenterTest { val state = awaitFirstItem() assertThat(state.customMapStyleUrl.isLoading()).isFalse() assertThat(state.trackUserLocation).isTrue() - assertThat(state.hasLocationPermission).isTrue() assertThat(state.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None)) } } @@ -155,7 +156,6 @@ class ShareLocationPresenterTest { }.test { val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isTrue() - assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.dialogState).isEqualTo(ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.None)) } } @@ -175,7 +175,6 @@ class ShareLocationPresenterTest { }.test { val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() - assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.dialogState).isEqualTo( ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionDenied) ) @@ -195,7 +194,6 @@ class ShareLocationPresenterTest { shareLocationPresenter.test { val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() - assertThat(initialState.hasLocationPermission).isFalse() assertThat(initialState.dialogState).isEqualTo( ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.PermissionRationale) ) @@ -216,7 +214,6 @@ class ShareLocationPresenterTest { shareLocationPresenter.test { val initialState = awaitFirstItem() assertThat(initialState.trackUserLocation).isFalse() - assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.dialogState).isEqualTo( ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled) ) diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt index 370ccac8ab..d0adf5e6bb 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/share/ShareLocationViewTest.kt @@ -120,7 +120,6 @@ class ShareLocationViewTest { setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled), - hasLocationPermission = true, eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), @@ -135,7 +134,6 @@ class ShareLocationViewTest { setShareLocationView( aShareLocationState( dialogState = ShareLocationState.Dialog.Constraints(LocationConstraintsDialogState.LocationServiceDisabled), - hasLocationPermission = true, eventSink = eventsRecorder ), navigateUp = EnsureNeverCalled(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index 8f55510128..23b7e9bf4d 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -14,6 +14,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.location.api.ShowLocationMode +import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.test.FakeActiveLiveLocationShareManager @@ -51,6 +52,7 @@ class DefaultShowLocationEntryPointTest { joinedRoom = joinedRoom, client = FakeMatrixClient(), liveLocationShareManager = FakeActiveLiveLocationShareManager(), + userLocationStateFactory = FakeUserLocationStateFactory(), ) }, analyticsService = FakeAnalyticsService(), diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index da3cea88f6..da8d766c88 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -15,6 +15,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.location.api.Location import io.element.android.features.location.api.ShowLocationMode import io.element.android.features.location.impl.aPermissionsState +import io.element.android.features.location.impl.common.FakeUserLocationStateFactory import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.permissions.FakePermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsEvents @@ -41,6 +42,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import kotlin.time.Duration.Companion.hours @OptIn(ExperimentalCoroutinesApi::class) class ShowLocationPresenterTest { @@ -76,26 +78,9 @@ class ShowLocationPresenterTest { joinedRoom = joinedRoom, client = client, liveLocationShareManager = liveLocationShareManager, + userLocationStateFactory = FakeUserLocationStateFactory(), ) - @Test - fun `emits initial state with no location permission`() = runTest { - fakePermissionsPresenter.givenState( - aPermissionsState( - permissions = PermissionsState.Permissions.NoneGranted, - shouldShowRationale = false, - ) - ) - - val presenter = createShowLocationPresenter() - presenter.test { - val initialState = awaitItem() - assertThat(initialState.customMapStyleUrl.isLoading()).isTrue() - assertThat(initialState.hasLocationPermission).isFalse() - assertThat(initialState.isTrackMyLocation).isFalse() - } - } - @Test fun `present - non-null customMapStyleUrl`() = runTest { val shareLocationPresenter = createShowLocationPresenter( @@ -112,35 +97,6 @@ class ShowLocationPresenterTest { } } - @Test - fun `emits initial state location permission denied once`() = runTest { - fakePermissionsPresenter.givenState( - aPermissionsState( - permissions = PermissionsState.Permissions.NoneGranted, - shouldShowRationale = true, - ) - ) - - val presenter = createShowLocationPresenter() - presenter.test { - val initialState = awaitItem() - assertThat(initialState.hasLocationPermission).isFalse() - assertThat(initialState.isTrackMyLocation).isFalse() - } - } - - @Test - fun `emits initial state with location permission`() = runTest { - fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted)) - - val presenter = createShowLocationPresenter() - presenter.test { - val initialState = awaitItem() - assertThat(initialState.hasLocationPermission).isTrue() - assertThat(initialState.isTrackMyLocation).isFalse() - } - } - @Test fun `emits initial state with partial location permission`() = runTest { fakePermissionsPresenter.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted)) @@ -148,7 +104,6 @@ class ShowLocationPresenterTest { val presenter = createShowLocationPresenter() presenter.test { val initialState = awaitItem() - assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.isTrackMyLocation).isFalse() } } @@ -176,7 +131,6 @@ class ShowLocationPresenterTest { presenter.test { skipItems(1) val initialState = awaitItem() - assertThat(initialState.hasLocationPermission).isTrue() assertThat(initialState.isTrackMyLocation).isFalse() initialState.eventSink(ShowLocationEvent.TrackMyLocation(true)) @@ -184,7 +138,6 @@ class ShowLocationPresenterTest { delay(1) - assertThat(trackMyLocationState.hasLocationPermission).isTrue() assertThat(trackMyLocationState.isTrackMyLocation).isTrue() // Swipe the map to switch mode @@ -192,7 +145,6 @@ class ShowLocationPresenterTest { val trackLocationDisabledState = awaitItem() assertThat(trackLocationDisabledState.dialogState).isEqualTo(LocationConstraintsDialogState.None) assertThat(trackLocationDisabledState.isTrackMyLocation).isFalse() - assertThat(trackLocationDisabledState.hasLocationPermission).isTrue() } } @@ -215,14 +167,12 @@ class ShowLocationPresenterTest { val trackLocationState = awaitItem() assertThat(trackLocationState.dialogState).isEqualTo(LocationConstraintsDialogState.PermissionRationale) assertThat(trackLocationState.isTrackMyLocation).isFalse() - assertThat(trackLocationState.hasLocationPermission).isFalse() // Dismiss the dialog initialState.eventSink(ShowLocationEvent.DismissDialog) val dialogDismissedState = awaitItem() assertThat(dialogDismissedState.dialogState).isEqualTo(LocationConstraintsDialogState.None) assertThat(dialogDismissedState.isTrackMyLocation).isFalse() - assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -244,7 +194,6 @@ class ShowLocationPresenterTest { val trackLocationState = awaitItem() assertThat(trackLocationState.dialogState).isEqualTo(LocationConstraintsDialogState.PermissionRationale) assertThat(trackLocationState.isTrackMyLocation).isFalse() - assertThat(trackLocationState.hasLocationPermission).isFalse() // Continue the dialog sends permission request to the permissions presenter trackLocationState.eventSink(ShowLocationEvent.RequestPermissions) @@ -271,14 +220,12 @@ class ShowLocationPresenterTest { val trackLocationState = awaitItem() assertThat(trackLocationState.dialogState).isEqualTo(LocationConstraintsDialogState.PermissionDenied) assertThat(trackLocationState.isTrackMyLocation).isFalse() - assertThat(trackLocationState.hasLocationPermission).isFalse() // Dismiss the dialog initialState.eventSink(ShowLocationEvent.DismissDialog) val dialogDismissedState = awaitItem() assertThat(dialogDismissedState.dialogState).isEqualTo(LocationConstraintsDialogState.None) assertThat(dialogDismissedState.isTrackMyLocation).isFalse() - assertThat(dialogDismissedState.hasLocationPermission).isFalse() } } @@ -326,7 +273,6 @@ class ShowLocationPresenterTest { val presenter = createShowLocationPresenter() presenter.test { val initialState = awaitItem() - assertThat(initialState.hasLocationPermission).isTrue() // Try to track location when location services are disabled initialState.eventSink(ShowLocationEvent.TrackMyLocation(true)) @@ -493,4 +439,25 @@ class ShowLocationPresenterTest { assertThat(state.isSheetDraggable).isFalse() } } + + @Test + fun `static mode never hides user location puck`() = runTest { + val presenter = createShowLocationPresenter() + presenter.test { + val state = awaitItem() + assertThat(state.hideUserLocationPuck).isFalse() + } + } + + @Test + fun `live mode does not hide user location puck when not sharing`() = runTest { + val presenter = createShowLocationPresenter( + mode = ShowLocationMode.Live(senderId = A_USER_ID), + ) + presenter.test { + val state = awaitItem() + assertThat(state.hideUserLocationPuck).isFalse() + } + } + } From f800bb5586828822b6917dca38c3637c0aeab576 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 3 Jun 2026 21:54:29 +0200 Subject: [PATCH 332/471] change : Hide UserLocaitonPuck when currentlySharing from this device (and follow the marker instead of the puck) --- .../impl/show/ShowLocationPresenter.kt | 12 +++++++-- .../impl/show/ShowLocationPresenterTest.kt | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt index 8ea857c6b9..e21fbb0605 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenter.kt @@ -200,7 +200,15 @@ import kotlinx.coroutines.launch } } } - val userLocationState = userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted) + val isCurrentlySharing by liveLocationShareManager.isCurrentlySharing(roomId = joinedRoom.roomId).collectAsState() + val hideUserLocationPuck = mode is ShowLocationMode.Live && isCurrentlySharing + val userLocationState = if (hideUserLocationPuck) { + // When sharing with this device, use the user LocationShareItem as source of data instead of the device. + val ownLocationShare by remember { derivedStateOf { updatedLocationShares.find { it.isOwnUser }?.location?.asMapLibreLocation() } } + UserLocationState(ownLocationShare) + } else { + userLocationStateFactory.create(hasLocationPermission = permissionsState.isAnyGranted) + } return ShowLocationState( customMapStyleUrl = customMapStyleUrl, dialogState = dialogState, @@ -210,7 +218,7 @@ import kotlinx.coroutines.launch userLocationState = userLocationState, isLive = mode is ShowLocationMode.Live, appName = appName, - hideUserLocationPuck = false, + hideUserLocationPuck = hideUserLocationPuck, eventSink = ::handleEvent, ) } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index da8d766c88..ab06cc5911 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -460,4 +460,30 @@ class ShowLocationPresenterTest { } } + @Test + fun `live mode user location state uses own share position when sharing`() = runTest { + val ownLiveLocationShare = aLiveLocationShare() + val fakeRoom = FakeJoinedRoom( + liveLocationSharesFlow = MutableStateFlow(listOf(ownLiveLocationShare)) + ) + val manager = FakeActiveLiveLocationShareManager( + startShareLambda = { _, _ -> Result.success(Unit) } + ) + manager.startShare(fakeRoom.roomId, 1.hours) + + val presenter = createShowLocationPresenter( + mode = ShowLocationMode.Live(senderId = A_USER_ID), + joinedRoom = fakeRoom, + liveLocationShareManager = manager, + ) + val ownLocation = ownLiveLocationShare.lastLocation?.geoUri?.let(Location::fromGeoUri) + presenter.test { + skipItems(1) + val state = awaitItem() + assertThat(state.hideUserLocationPuck).isTrue() + val location = requireNotNull(state.userLocationState.location) + assertThat(location.position.value.latitude).isEqualTo(ownLocation?.lat) + assertThat(location.position.value.longitude).isEqualTo(ownLocation?.lon) + } + } } From 1b2bcd8ee548f45742fdfcb3809685ba345c7829 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 3 Jun 2026 20:13:05 +0000 Subject: [PATCH 333/471] Update screenshots --- .../features.location.impl.show_ShowLocationView_Day_6_en.png | 4 ++-- .../features.location.impl.show_ShowLocationView_Day_7_en.png | 4 ++-- .../features.location.impl.show_ShowLocationView_Day_8_en.png | 3 --- ...eatures.location.impl.show_ShowLocationView_Night_6_en.png | 4 ++-- ...eatures.location.impl.show_ShowLocationView_Night_7_en.png | 4 ++-- ...eatures.location.impl.show_ShowLocationView_Night_8_en.png | 3 --- 6 files changed, 8 insertions(+), 14 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png 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 46226555db..ceb1513af6 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:cdf7b194a075902ab9434e272865293535ef39370fbb9cb172b3cf8774850c73 -size 19104 +oid sha256:8988c700db517eef71d7f42c8e21ac819f51c92fb88c0c25cb400be7a5326c22 +size 19228 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 ceb1513af6..e01e90f7d0 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:8988c700db517eef71d7f42c8e21ac819f51c92fb88c0c25cb400be7a5326c22 -size 19228 +oid sha256:dea2fa898a6f9b921eca0931d7380814b31ba858b88c90ba1fbab6332b4c8ebb +size 20722 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png deleted file mode 100644 index e01e90f7d0..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Day_8_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dea2fa898a6f9b921eca0931d7380814b31ba858b88c90ba1fbab6332b4c8ebb -size 20722 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 eed60f472d..d3ee3b9e22 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:da601c01dd487f9c66f78ada91954398e1dcdf699f1ba4f6d8f7661f7b8cc4b7 -size 18715 +oid sha256:ca86fd5eea8c05a52fee801e1ca61c2e4e205ad9874e06d69b1d1f674585f87b +size 18842 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 d3ee3b9e22..547e7ba364 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:ca86fd5eea8c05a52fee801e1ca61c2e4e205ad9874e06d69b1d1f674585f87b -size 18842 +oid sha256:f9f64bc6bb5956459f8943c05bbf0aa6ee9e97e3103713f34d49c013f86376a9 +size 18878 diff --git a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png deleted file mode 100644 index 547e7ba364..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.location.impl.show_ShowLocationView_Night_8_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f9f64bc6bb5956459f8943c05bbf0aa6ee9e97e3103713f34d49c013f86376a9 -size 18878 From 64c04cefe0d64e40bc29003a508bad4cdd156f21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 05:12:17 +0000 Subject: [PATCH 334/471] Update dependencyAnalysis to v3.14.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9657aaf15d..e4e1a44304 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -51,7 +51,7 @@ telephoto = "0.19.0" haze = "1.7.2" # Dependency analysis -dependencyAnalysis = "3.12.0" +dependencyAnalysis = "3.14.0" # DI metro = "1.1.1" From 1717cc7370e12fae3cd94fe4b20fca9296ff28ec Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 4 Jun 2026 08:34:55 +0100 Subject: [PATCH 335/471] Fix some string mappings for link new device errors (#6675) * Fix some string mappings for link new device errors * Update snapshots * Add appName * Iterate --------- Co-authored-by: Jorge Martin Espinosa Co-authored-by: Benoit Marty --- .../linknewdevice/impl/screens/error/ErrorView.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt index cc94951dbd..7b830626f6 100644 --- a/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt +++ b/features/linknewdevice/impl/src/main/kotlin/io/element/android/features/linknewdevice/impl/screens/error/ErrorView.kt @@ -73,10 +73,10 @@ fun ErrorView( @Composable private fun titleText(errorScreenType: ErrorScreenType, appName: String) = when (errorScreenType) { - ErrorScreenType.Cancelled -> stringResource(R.string.screen_qr_code_login_error_cancelled_title) + ErrorScreenType.Cancelled -> stringResource(R.string.screen_link_new_device_error_request_cancelled_title) ErrorScreenType.Declined -> stringResource(R.string.screen_qr_code_login_error_declined_title) - ErrorScreenType.Expired -> stringResource(R.string.screen_qr_code_login_error_expired_title) - ErrorScreenType.ProtocolNotSupported -> stringResource(R.string.screen_qr_code_login_error_linking_not_suported_title) + ErrorScreenType.Expired -> stringResource(R.string.screen_link_new_device_error_request_timeout_title) + ErrorScreenType.ProtocolNotSupported -> stringResource(R.string.screen_link_new_device_error_not_supported_title) ErrorScreenType.InsecureChannelDetected -> stringResource(id = R.string.screen_qr_code_login_connection_note_secure_state_title) ErrorScreenType.Mismatch2Digits -> stringResource(id = R.string.screen_link_new_device_wrong_number_title) ErrorScreenType.SlidingSyncNotAvailable -> stringResource(id = R.string.screen_qr_code_login_error_sliding_sync_not_supported_title, appName) @@ -86,10 +86,10 @@ private fun titleText(errorScreenType: ErrorScreenType, appName: String) = when @Composable private fun subtitleText(errorScreenType: ErrorScreenType, appName: String) = when (errorScreenType) { - ErrorScreenType.Cancelled -> stringResource(R.string.screen_qr_code_login_error_cancelled_subtitle) + ErrorScreenType.Cancelled -> stringResource(R.string.screen_link_new_device_error_request_cancelled_subtitle) ErrorScreenType.Declined -> stringResource(R.string.screen_qr_code_login_error_declined_subtitle) - ErrorScreenType.Expired -> stringResource(R.string.screen_qr_code_login_error_expired_subtitle) - ErrorScreenType.ProtocolNotSupported -> stringResource(R.string.screen_qr_code_login_error_linking_not_suported_subtitle, appName) + ErrorScreenType.Expired -> stringResource(R.string.screen_link_new_device_error_request_timeout_subtitle) + ErrorScreenType.ProtocolNotSupported -> stringResource(R.string.screen_link_new_device_error_not_supported_subtitle) ErrorScreenType.Mismatch2Digits -> stringResource(id = R.string.screen_link_new_device_wrong_number_subtitle) ErrorScreenType.InsecureChannelDetected -> stringResource(id = R.string.screen_qr_code_login_connection_note_secure_state_description) ErrorScreenType.SlidingSyncNotAvailable -> stringResource(id = R.string.screen_qr_code_login_error_sliding_sync_not_supported_subtitle, appName) From fdfd5ce72edad97f08da7a8e9f527a003837b94e Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 4 Jun 2026 07:53:12 +0000 Subject: [PATCH 336/471] Update screenshots --- ...es.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png | 4 ++-- ....linknewdevice.impl.screens.error_ErrorView_Night_3_en.png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png index 19a824127e..ee619462ec 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d30e200c3e73775ed7081d3fbdc9f8843c5a5bf9e5f1b9ab3535cc6f0ea7f1f6 -size 35946 +oid sha256:17c58ce74a1b198f8659636d180a98152bd66be212f381afd652f615bd847a7f +size 28795 diff --git a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png index f1f3896ab8..21fe725606 100644 --- a/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.linknewdevice.impl.screens.error_ErrorView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a78b0677d896de960ce8ce7c5818b1d52004c91917c73c460a605e5e1342554f -size 35129 +oid sha256:5493215960a23d305a2f9f26e39b307c735167583c20ac3fafb1b67e89aa1d12 +size 28124 From 371e69305385209a8a2ee89e8fe6e99b4d926db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 11:06:42 +0200 Subject: [PATCH 337/471] Setting version for the release 26.06.0 --- plugins/src/main/kotlin/Versions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 5e9a8694be..69d7791e6b 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -39,13 +39,13 @@ private const val versionYear = 26 * Month of the version on 2 digits. Value must be in [1,12]. * Do not update this value. it is updated by the release script. */ -private const val versionMonth = 5 +private const val versionMonth = 6 /** * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 2 +private const val versionReleaseNumber = 0 object Versions { /** From c3fd5a80b5fc4002216ec75480df3d8b488a7a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 11:09:38 +0200 Subject: [PATCH 338/471] Adding fastlane file for version 26.06.0 --- fastlane/metadata/android/en-US/changelogs/202606000.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202606000.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202606000.txt b/fastlane/metadata/android/en-US/changelogs/202606000.txt new file mode 100644 index 0000000000..6c68999e99 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202606000.txt @@ -0,0 +1,2 @@ +Main changes in this version: added image editing before sending and an option for custom notification sounds, improved start times, several other bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From a91e1fa2ae7a84a6f7daa6cb5d489b39ea818b4f Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 4 Jun 2026 15:08:19 +0200 Subject: [PATCH 339/471] Make Vulkan a *not required* feature (#6961) * Make Vulkan a *not required* feature * Add `DeviceHasVulkanSupport` helper to check this on runtime * When trying to open a screen with a map in it, check if vulkan is supported and display a new error dialog in that case * Fix random lint issue * Update screenshots --------- Co-authored-by: ElementBot --- .../home/impl/components/RoomSummaryRow.kt | 4 +- .../api/RenderingMapsNotSupportedDialog.kt | 30 +++++++++ .../impl/src/main/AndroidManifest.xml | 10 ++- .../messages/impl/MessagesFlowNode.kt | 64 ++++++++++++++----- .../impl/DefaultMessagesEntryPointTest.kt | 3 + .../system/DeviceHasVulkanSupport.kt | 31 +++++++++ .../src/main/res/values/localazy.xml | 2 + ...nderingMapsNotSupportedDialog_Day_0_en.png | 3 + ...eringMapsNotSupportedDialog_Night_0_en.png | 3 + 9 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/RenderingMapsNotSupportedDialog.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DeviceHasVulkanSupport.kt create mode 100644 tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Night_0_en.png diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index 934f60aa29..ba9a204a64 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -75,10 +75,10 @@ internal fun RoomSummaryRow( room: RoomListRoomSummary, hideInviteAvatars: Boolean, isInviteSeen: Boolean, - showUnreadCount: Boolean = false, onClick: (RoomListRoomSummary) -> Unit, - eventSink: (RoomListEvent) -> Unit, modifier: Modifier = Modifier, + showUnreadCount: Boolean = false, + eventSink: (RoomListEvent) -> Unit, ) { Box(modifier = modifier) { when (room.displayType) { diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/RenderingMapsNotSupportedDialog.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/RenderingMapsNotSupportedDialog.kt new file mode 100644 index 0000000000..a4d65763ab --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/RenderingMapsNotSupportedDialog.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.api + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +fun RenderingMapsNotSupportedDialog(onSubmit: () -> Unit) { + ErrorDialog( + title = stringResource(CommonStrings.vulkan_not_supported_dialog_title_android), + content = stringResource(CommonStrings.vulkan_not_supported_dialog_content_android), + onSubmit = onSubmit, + ) +} + +@PreviewsDayNight +@Composable +internal fun RenderingMapsNotSupportedDialogPreview() = ElementPreview { + RenderingMapsNotSupportedDialog(onSubmit = {}) +} diff --git a/features/location/impl/src/main/AndroidManifest.xml b/features/location/impl/src/main/AndroidManifest.xml index e92ca68077..e56a13e644 100644 --- a/features/location/impl/src/main/AndroidManifest.xml +++ b/features/location/impl/src/main/AndroidManifest.xml @@ -5,13 +5,21 @@ ~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. ~ Please see LICENSE files in the repository root for full details. --> - + + + + ( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialTarget.toNavTarget(), @@ -188,6 +194,8 @@ class MessagesFlowNode( private val callback: MessagesEntryPoint.Callback = callback() + private var displayVulkanNotSupportedError by mutableStateOf(false) + override fun onBuilt() { super.onBuilt() lifecycle.subscribe( @@ -267,7 +275,11 @@ class MessagesFlowNode( } override fun navigateToSendLocation() { - backstack.push(NavTarget.SendLocation(Timeline.Mode.Live)) + if (hasVulkanSupport()) { + backstack.push(NavTarget.SendLocation(Timeline.Mode.Live)) + } else { + displayVulkanNotSupportedError = true + } } override fun navigateToCreatePoll() { @@ -279,7 +291,11 @@ class MessagesFlowNode( } override fun navigateToCurrentLiveLocation() { - backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + if (hasVulkanSupport()) { + backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + } else { + displayVulkanNotSupportedError = true + } } override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { @@ -506,7 +522,11 @@ class MessagesFlowNode( } override fun navigateToSendLocation() { - backstack.push(NavTarget.SendLocation(Timeline.Mode.Thread(navTarget.threadRootId))) + if (hasVulkanSupport()) { + backstack.push(NavTarget.SendLocation(Timeline.Mode.Thread(navTarget.threadRootId))) + } else { + displayVulkanNotSupportedError = true + } } override fun navigateToCreatePoll() { @@ -518,7 +538,11 @@ class MessagesFlowNode( } override fun navigateToCurrentLiveLocation() { - backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + if (hasVulkanSupport()) { + backstack.push(NavTarget.LocationViewer(ShowLocationMode.Live(senderId = sessionId))) + } else { + displayVulkanNotSupportedError = true + } } override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) { @@ -607,18 +631,23 @@ class MessagesFlowNode( ) } is TimelineItemLocationContent -> { - val mode = when (event.content.mode) { - is TimelineItemLocationContent.Mode.Live -> ShowLocationMode.Live(event.senderId) - is TimelineItemLocationContent.Mode.Static -> ShowLocationMode.Static( - location = event.content.mode.location, - senderName = event.safeSenderName, - senderId = event.senderId, - senderAvatarUrl = event.senderAvatar.url, - timestamp = event.sentTimeMillis, - assetType = event.content.assetType, - ) + if (hasVulkanSupport()) { + val mode = when (event.content.mode) { + is TimelineItemLocationContent.Mode.Live -> ShowLocationMode.Live(event.senderId) + is TimelineItemLocationContent.Mode.Static -> ShowLocationMode.Static( + location = event.content.mode.location, + senderName = event.safeSenderName, + senderId = event.senderId, + senderAvatarUrl = event.senderAvatar.url, + timestamp = event.sentTimeMillis, + assetType = event.content.assetType, + ) + } + NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable() } + } else { + displayVulkanNotSupportedError = true + null } - NavTarget.LocationViewer(mode = mode).takeIf { locationService.isServiceAvailable() } } else -> null } @@ -691,6 +720,11 @@ class MessagesFlowNode( @Composable override fun View(modifier: Modifier) { mentionSpanTheme.updateStyles() + + if (displayVulkanNotSupportedError) { + RenderingMapsNotSupportedDialog { displayVulkanNotSupportedError = false } + } + CompositionLocalProvider( LocalMentionSpanUpdater provides mentionSpanUpdater ) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt index dc50fca2c3..9679e23a23 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt @@ -23,6 +23,7 @@ import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.impl.pinned.banner.createPinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.createTimelineController import io.element.android.features.poll.test.create.FakeCreatePollEntryPoint +import io.element.android.libraries.androidutils.system.DeviceHasVulkanSupport import io.element.android.libraries.dateformatter.test.FakeDateFormatter import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -42,6 +43,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import io.element.android.tests.testutils.testCoroutineDispatchers +import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -85,6 +87,7 @@ class DefaultMessagesEntryPointTest { knockRequestsListEntryPoint = FakeKnockRequestsListEntryPoint(), dateFormatter = FakeDateFormatter(), coroutineDispatchers = testCoroutineDispatchers(), + hasVulkanSupport = DeviceHasVulkanSupport(mockk(relaxed = true)) ) } val callback = object : MessagesEntryPoint.Callback { diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DeviceHasVulkanSupport.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DeviceHasVulkanSupport.kt new file mode 100644 index 0000000000..e8e5edcca0 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/DeviceHasVulkanSupport.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.system + +import android.content.Context +import dev.zacsweers.metro.Inject +import io.element.android.libraries.di.annotations.ApplicationContext + +private const val VULKAN_VERSION_1_0 = 0x400003 + +/** + * Checks if the device supports Vulkan 1.0. + * + * This is needed for the location screens that contain maps using MapLibre UI components. + * + * Needed until https://github.com/maplibre/maplibre-native/issues/3079 is resolved and we can automatically choose between OpenGL and Vulkan renderers, + * or no devices support OpenGL anymore. + */ +@Inject +class DeviceHasVulkanSupport( + @ApplicationContext private val context: Context, +) { + operator fun invoke(): Boolean { + return context.packageManager.hasSystemFeature("android.hardware.vulkan.version", VULKAN_VERSION_1_0) + } +} diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 823c0fad72..75b6ab1ee7 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -546,4 +546,6 @@ Are you sure you want to continue?" "You don\'t have access to this message" "Unable to decrypt message" "This message was blocked either because you did not verify your device or because the sender needs to verify your digital identity." + "Your device is too old, a device with Android 8 or newer is required." + "Rendering maps is not supported" diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Day_0_en.png new file mode 100644 index 0000000000..cf35ad0fa8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f64455657f0f8a5ac7eec71ee206721a79b25dacf470d105a0e6497e570204a +size 23954 diff --git a/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Night_0_en.png new file mode 100644 index 0000000000..41cdb80dc8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.location.api_RenderingMapsNotSupportedDialog_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43bd5f04f170372b120fa2223c632d6a969c45b6d4fe61b161a7109318f9941f +size 22658 From 2552f12370bc1b319ebf2c14a128c90fbb4857d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:20:35 +0000 Subject: [PATCH 340/471] Update dependency com.github.matrix-org:matrix-analytics-events to v0.36.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9657aaf15d..0fb49f51bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -229,7 +229,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" posthog = "com.posthog:posthog-android:3.47.0" sentry = "io.sentry:sentry-android:8.41.0" # main branch can be tested replacing the version with main-SNAPSHOT -matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.36.0" +matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.36.1" # Emojibase matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.5.3" From 092448d5842b98693870f862625727e03f2d291e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 16:13:28 +0200 Subject: [PATCH 341/471] Setting version for the release 26.06.1 --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 69d7791e6b..f87eed1a40 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 6 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 0 +private const val versionReleaseNumber = 1 object Versions { /** From 4749ec3112dcf69514d6075f7fc7ffd77f329874 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2026 16:16:29 +0200 Subject: [PATCH 342/471] Replace "Edit" button with Crop icon. --- .../preview/AttachmentsPreviewView.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 926106a5ee..7a02060ccf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.tooling.preview.Preview 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.R import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError @@ -57,11 +58,12 @@ import io.element.android.libraries.designsystem.modifiers.niceClickable import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreviewDark 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.ListItem import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Switch 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.designsystem.utils.CommonDrawables import io.element.android.libraries.mediaviewer.api.local.LocalMedia @@ -163,10 +165,14 @@ fun AttachmentsPreviewView( }, actions = { if (state.canEditImage && canShowEditAction) { - TextButton( - stringResource(CommonStrings.action_edit), - onClick = ::postOpenImageEditor - ) + IconButton( + onClick = ::postOpenImageEditor, + ) { + Icon( + imageVector = CompoundIcons.Crop(), + contentDescription = stringResource(CommonStrings.action_edit), + ) + } } } ) From 1592ab9686f53fe5a5f464b8463c05f0d56e3481 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 4 Jun 2026 16:12:44 +0200 Subject: [PATCH 343/471] Sync compound tokens https://github.com/element-hq/compound-design-tokens/releases/tag/v10.2.2 --- libraries/compound/src/main/assets/theme.iife.js | 2 +- .../compound/src/main/res/drawable/ic_compound_crop.xml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/compound/src/main/assets/theme.iife.js b/libraries/compound/src/main/assets/theme.iife.js index 02fd99637a..41e18ba6a0 100644 --- a/libraries/compound/src/main/assets/theme.iife.js +++ b/libraries/compound/src/main/assets/theme.iife.js @@ -1 +1 @@ -var CompoundTheme=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;lt[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),{min:u,max:d}=Math,f=(e,t=0,n=1)=>u(d(t,e),n),p=e=>{e._clipped=!1,e._unclipped=e.slice(0);for(let t=0;t<=3;t++)t<3?((e[t]<0||e[t]>255)&&(e._clipped=!0),e[t]=f(e[t],0,255)):t===3&&(e[t]=f(e[t],0,1));return e},m={};for(let e of[`Boolean`,`Number`,`String`,`Function`,`Array`,`Date`,`RegExp`,`Undefined`,`Null`])m[`[object ${e}]`]=e.toLowerCase();function h(e){return m[Object.prototype.toString.call(e)]||`object`}var g=(e,t=null)=>e.length>=3?Array.prototype.slice.call(e):h(e[0])==`object`&&t?t.split(``).filter(t=>e[0][t]!==void 0).map(t=>e[0][t]):e[0].slice(0),_=e=>{if(e.length<2)return null;let t=e.length-1;return h(e[t])==`string`?e[t].toLowerCase():null},{PI:v,min:y,max:b}=Math,x=e=>Math.round(e*100)/100,S=e=>Math.round(e*100)/100,C=v*2,w=v/3,T=v/180,ee=180/v;function E(e){return[...e.slice(0,3).reverse(),...e.slice(3)]}var D={format:{},autodetect:[]},O=class{constructor(...e){let t=this;if(h(e[0])===`object`&&e[0].constructor&&e[0].constructor===this.constructor)return e[0];let n=_(e),r=!1;if(!n){r=!0,D.sorted||=(D.autodetect=D.autodetect.sort((e,t)=>t.p-e.p),!0);for(let t of D.autodetect)if(n=t.test(...e),n)break}if(D.format[n])t._rgb=p(D.format[n].apply(null,r?e:e.slice(0,-1)));else throw Error(`unknown format: `+e);t._rgb.length===3&&t._rgb.push(1)}toString(){return h(this.hex)==`function`?this.hex():`[${this._rgb.join(`,`)}]`}},te=`3.2.0`,k=(...e)=>new O(...e);k.version=te;var A={aliceblue:`#f0f8ff`,antiquewhite:`#faebd7`,aqua:`#00ffff`,aquamarine:`#7fffd4`,azure:`#f0ffff`,beige:`#f5f5dc`,bisque:`#ffe4c4`,black:`#000000`,blanchedalmond:`#ffebcd`,blue:`#0000ff`,blueviolet:`#8a2be2`,brown:`#a52a2a`,burlywood:`#deb887`,cadetblue:`#5f9ea0`,chartreuse:`#7fff00`,chocolate:`#d2691e`,coral:`#ff7f50`,cornflowerblue:`#6495ed`,cornsilk:`#fff8dc`,crimson:`#dc143c`,cyan:`#00ffff`,darkblue:`#00008b`,darkcyan:`#008b8b`,darkgoldenrod:`#b8860b`,darkgray:`#a9a9a9`,darkgreen:`#006400`,darkgrey:`#a9a9a9`,darkkhaki:`#bdb76b`,darkmagenta:`#8b008b`,darkolivegreen:`#556b2f`,darkorange:`#ff8c00`,darkorchid:`#9932cc`,darkred:`#8b0000`,darksalmon:`#e9967a`,darkseagreen:`#8fbc8f`,darkslateblue:`#483d8b`,darkslategray:`#2f4f4f`,darkslategrey:`#2f4f4f`,darkturquoise:`#00ced1`,darkviolet:`#9400d3`,deeppink:`#ff1493`,deepskyblue:`#00bfff`,dimgray:`#696969`,dimgrey:`#696969`,dodgerblue:`#1e90ff`,firebrick:`#b22222`,floralwhite:`#fffaf0`,forestgreen:`#228b22`,fuchsia:`#ff00ff`,gainsboro:`#dcdcdc`,ghostwhite:`#f8f8ff`,gold:`#ffd700`,goldenrod:`#daa520`,gray:`#808080`,green:`#008000`,greenyellow:`#adff2f`,grey:`#808080`,honeydew:`#f0fff0`,hotpink:`#ff69b4`,indianred:`#cd5c5c`,indigo:`#4b0082`,ivory:`#fffff0`,khaki:`#f0e68c`,laserlemon:`#ffff54`,lavender:`#e6e6fa`,lavenderblush:`#fff0f5`,lawngreen:`#7cfc00`,lemonchiffon:`#fffacd`,lightblue:`#add8e6`,lightcoral:`#f08080`,lightcyan:`#e0ffff`,lightgoldenrod:`#fafad2`,lightgoldenrodyellow:`#fafad2`,lightgray:`#d3d3d3`,lightgreen:`#90ee90`,lightgrey:`#d3d3d3`,lightpink:`#ffb6c1`,lightsalmon:`#ffa07a`,lightseagreen:`#20b2aa`,lightskyblue:`#87cefa`,lightslategray:`#778899`,lightslategrey:`#778899`,lightsteelblue:`#b0c4de`,lightyellow:`#ffffe0`,lime:`#00ff00`,limegreen:`#32cd32`,linen:`#faf0e6`,magenta:`#ff00ff`,maroon:`#800000`,maroon2:`#7f0000`,maroon3:`#b03060`,mediumaquamarine:`#66cdaa`,mediumblue:`#0000cd`,mediumorchid:`#ba55d3`,mediumpurple:`#9370db`,mediumseagreen:`#3cb371`,mediumslateblue:`#7b68ee`,mediumspringgreen:`#00fa9a`,mediumturquoise:`#48d1cc`,mediumvioletred:`#c71585`,midnightblue:`#191970`,mintcream:`#f5fffa`,mistyrose:`#ffe4e1`,moccasin:`#ffe4b5`,navajowhite:`#ffdead`,navy:`#000080`,oldlace:`#fdf5e6`,olive:`#808000`,olivedrab:`#6b8e23`,orange:`#ffa500`,orangered:`#ff4500`,orchid:`#da70d6`,palegoldenrod:`#eee8aa`,palegreen:`#98fb98`,paleturquoise:`#afeeee`,palevioletred:`#db7093`,papayawhip:`#ffefd5`,peachpuff:`#ffdab9`,peru:`#cd853f`,pink:`#ffc0cb`,plum:`#dda0dd`,powderblue:`#b0e0e6`,purple:`#800080`,purple2:`#7f007f`,purple3:`#a020f0`,rebeccapurple:`#663399`,red:`#ff0000`,rosybrown:`#bc8f8f`,royalblue:`#4169e1`,saddlebrown:`#8b4513`,salmon:`#fa8072`,sandybrown:`#f4a460`,seagreen:`#2e8b57`,seashell:`#fff5ee`,sienna:`#a0522d`,silver:`#c0c0c0`,skyblue:`#87ceeb`,slateblue:`#6a5acd`,slategray:`#708090`,slategrey:`#708090`,snow:`#fffafa`,springgreen:`#00ff7f`,steelblue:`#4682b4`,tan:`#d2b48c`,teal:`#008080`,thistle:`#d8bfd8`,tomato:`#ff6347`,turquoise:`#40e0d0`,violet:`#ee82ee`,wheat:`#f5deb3`,white:`#ffffff`,whitesmoke:`#f5f5f5`,yellow:`#ffff00`,yellowgreen:`#9acd32`},ne=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,re=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,ie=e=>{if(e.match(ne)){(e.length===4||e.length===7)&&(e=e.substr(1)),e.length===3&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let t=parseInt(e,16);return[t>>16,t>>8&255,t&255,1]}if(e.match(re)){(e.length===5||e.length===9)&&(e=e.substr(1)),e.length===4&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]+e[3]+e[3]);let t=parseInt(e,16);return[t>>24&255,t>>16&255,t>>8&255,Math.round((t&255)/255*100)/100]}throw Error(`unknown hex color: ${e}`)},{round:ae}=Math,oe=(...e)=>{let[t,n,r,i]=g(e,`rgba`),a=_(e)||`auto`;i===void 0&&(i=1),a===`auto`&&(a=i<1?`rgba`:`rgb`),t=ae(t),n=ae(n),r=ae(r);let o=`000000`+(t<<16|n<<8|r).toString(16);o=o.substr(o.length-6);let s=`0`+ae(i*255).toString(16);switch(s=s.substr(s.length-2),a.toLowerCase()){case`rgba`:return`#${o}${s}`;case`argb`:return`#${s}${o}`;default:return`#${o}`}};O.prototype.name=function(){let e=oe(this._rgb,`rgb`);for(let t of Object.keys(A))if(A[t]===e)return t.toLowerCase();return e},D.format.named=e=>{if(e=e.toLowerCase(),A[e])return ie(A[e]);throw Error(`unknown color name: `+e)},D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&A[e.toLowerCase()])return`named`}}),O.prototype.alpha=function(e,t=!1){return e!==void 0&&h(e)===`number`?t?(this._rgb[3]=e,this):new O([this._rgb[0],this._rgb[1],this._rgb[2],e],`rgb`):this._rgb[3]},O.prototype.clipped=function(){return this._rgb._clipped||!1};var j={Kn:18,labWhitePoint:`d65`,Xn:.95047,Yn:1,Zn:1.08883,t0:.137931034,t1:.206896552,t2:.12841855,t3:.008856452,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},se=new Map([[`a`,[1.0985,.35585]],[`b`,[1.0985,.35585]],[`c`,[.98074,1.18232]],[`d50`,[.96422,.82521]],[`d55`,[.95682,.92149]],[`d65`,[.95047,1.08883]],[`e`,[1,1,1]],[`f2`,[.99186,.67393]],[`f7`,[.95041,1.08747]],[`f11`,[1.00962,.6435]],[`icc`,[.96422,.82521]]]);function M(e){let t=se.get(String(e).toLowerCase());if(!t)throw Error(`unknown Lab illuminant `+e);j.labWhitePoint=e,j.Xn=t[0],j.Zn=t[1]}function ce(){return j.labWhitePoint}var N=(...e)=>{e=g(e,`lab`);let[t,n,r]=e,[i,a,o]=le(t,n,r),[s,c,l]=de(i,a,o);return[s,c,l,e.length>3?e[3]:1]},le=(e,t,n)=>{let{kE:r,kK:i,kKE:a,Xn:o,Yn:s,Zn:c}=j,l=(e+16)/116,u=.002*t+l,d=l-.005*n,f=u*u*u,p=d*d*d,m=f>r?f:(116*u-16)/i,h=e>a?((e+16)/116)**3:e/i,g=p>r?p:(116*d-16)/i;return[m*o,h*s,g*c]},ue=e=>{let t=Math.sign(e);return e=Math.abs(e),(e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055)*t},de=(e,t,n)=>{let{MtxAdaptMa:r,MtxAdaptMaI:i,MtxXYZ2RGB:a,RefWhiteRGB:o,Xn:s,Yn:c,Zn:l}=j,u=s*r.m00+c*r.m10+l*r.m20,d=s*r.m01+c*r.m11+l*r.m21,f=s*r.m02+c*r.m12+l*r.m22,p=o.X*r.m00+o.Y*r.m10+o.Z*r.m20,m=o.X*r.m01+o.Y*r.m11+o.Z*r.m21,h=o.X*r.m02+o.Y*r.m12+o.Z*r.m22,g=(e*r.m00+t*r.m10+n*r.m20)*(p/u),_=(e*r.m01+t*r.m11+n*r.m21)*(m/d),v=(e*r.m02+t*r.m12+n*r.m22)*(h/f),y=g*i.m00+_*i.m10+v*i.m20,b=g*i.m01+_*i.m11+v*i.m21,x=g*i.m02+_*i.m12+v*i.m22,S=ue(y*a.m00+b*a.m10+x*a.m20),C=ue(y*a.m01+b*a.m11+x*a.m21),w=ue(y*a.m02+b*a.m12+x*a.m22);return[S*255,C*255,w*255]},fe=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=he(t,n,r),[c,l,u]=pe(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function pe(e,t,n){let{Xn:r,Yn:i,Zn:a,kE:o,kK:s}=j,c=e/r,l=t/i,u=n/a,d=c>o?c**(1/3):(s*c+16)/116,f=l>o?l**(1/3):(s*l+16)/116,p=u>o?u**(1/3):(s*u+16)/116;return[116*f-16,500*(d-f),200*(f-p)]}function me(e){let t=Math.sign(e);return e=Math.abs(e),(e<=.04045?e/12.92:((e+.055)/1.055)**2.4)*t}var he=(e,t,n)=>{e=me(e/255),t=me(t/255),n=me(n/255);let{MtxRGB2XYZ:r,MtxAdaptMa:i,MtxAdaptMaI:a,Xn:o,Yn:s,Zn:c,As:l,Bs:u,Cs:d}=j,f=e*r.m00+t*r.m10+n*r.m20,p=e*r.m01+t*r.m11+n*r.m21,m=e*r.m02+t*r.m12+n*r.m22,h=o*i.m00+s*i.m10+c*i.m20,g=o*i.m01+s*i.m11+c*i.m21,_=o*i.m02+s*i.m12+c*i.m22,v=f*i.m00+p*i.m10+m*i.m20,y=f*i.m01+p*i.m11+m*i.m21,b=f*i.m02+p*i.m12+m*i.m22;return v*=h/l,y*=g/u,b*=_/d,f=v*a.m00+y*a.m10+b*a.m20,p=v*a.m01+y*a.m11+b*a.m21,m=v*a.m02+y*a.m12+b*a.m22,[f,p,m]};O.prototype.lab=function(){return fe(this._rgb)},Object.assign(k,{lab:(...e)=>new O(...e,`lab`),getLabWhitePoint:ce,setLabWhitePoint:M}),D.format.lab=N,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`lab`),h(e)===`array`&&e.length===3)return`lab`}}),O.prototype.darken=function(e=1){let t=this,n=t.lab();return n[0]-=j.Kn*e,new O(n,`lab`).alpha(t.alpha(),!0)},O.prototype.brighten=function(e=1){return this.darken(-e)},O.prototype.darker=O.prototype.darken,O.prototype.brighter=O.prototype.brighten,O.prototype.get=function(e){let[t,n]=e.split(`.`),r=this[t]();if(n){let e=t.indexOf(n)-(t.substr(0,2)===`ok`?2:0);if(e>-1)return r[e];throw Error(`unknown channel ${n} in mode ${t}`)}else return r};var{pow:ge}=Math,_e=1e-7,ve=20;O.prototype.luminance=function(e,t=`rgb`){if(e!==void 0&&h(e)===`number`){if(e===0)return new O([0,0,0,this._rgb[3]],`rgb`);if(e===1)return new O([255,255,255,this._rgb[3]],`rgb`);let n=this.luminance(),r=ve,i=(n,a)=>{let o=n.interpolate(a,.5,t),s=o.luminance();return Math.abs(e-s)<_e||!r--?o:s>e?i(n,o):i(o,a)};return new O([...(n>e?i(new O([0,0,0]),this):i(this,new O([255,255,255]))).rgb(),this._rgb[3]])}return ye(...this._rgb.slice(0,3))};var ye=(e,t,n)=>(e=be(e),t=be(t),n=be(n),.2126*e+.7152*t+.0722*n),be=e=>(e/=255,e<=.03928?e/12.92:ge((e+.055)/1.055,2.4)),P={},F=(e,t,n=.5,...r)=>{let i=r[0]||`lrgb`;if(!P[i]&&!r.length&&(i=Object.keys(P)[0]),!P[i])throw Error(`interpolation mode ${i} is not defined`);return h(e)!==`object`&&(e=new O(e)),h(t)!==`object`&&(t=new O(t)),P[i](e,t,n).alpha(e.alpha()+n*(t.alpha()-e.alpha()))};O.prototype.mix=O.prototype.interpolate=function(e,t=.5,...n){return F(this,e,t,...n)},O.prototype.premultiply=function(e=!1){let t=this._rgb,n=t[3];return e?(this._rgb=[t[0]*n,t[1]*n,t[2]*n,n],this):new O([t[0]*n,t[1]*n,t[2]*n,n],`rgb`)};var{sin:xe,cos:Se}=Math,Ce=(...e)=>{let[t,n,r]=g(e,`lch`);return isNaN(r)&&(r=0),r*=T,[t,Se(r)*n,xe(r)*n]},we=(...e)=>{e=g(e,`lch`);let[t,n,r]=e,[i,a,o]=Ce(t,n,r),[s,c,l]=N(i,a,o);return[s,c,l,e.length>3?e[3]:1]},Te=(...e)=>we(...E(g(e,`hcl`))),{sqrt:Ee,atan2:De,round:Oe}=Math,ke=(...e)=>{let[t,n,r]=g(e,`lab`),i=Ee(n*n+r*r),a=(De(r,n)*ee+360)%360;return Oe(i*1e4)===0&&(a=NaN),[t,i,a]},Ae=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=fe(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};O.prototype.lch=function(){return Ae(this._rgb)},O.prototype.hcl=function(){return E(Ae(this._rgb))},Object.assign(k,{lch:(...e)=>new O(...e,`lch`),hcl:(...e)=>new O(...e,`hcl`)}),D.format.lch=we,D.format.hcl=Te,[`lch`,`hcl`].forEach(e=>D.autodetect.push({p:2,test:(...t)=>{if(t=g(t,e),h(t)===`array`&&t.length===3)return e}})),O.prototype.saturate=function(e=1){let t=this,n=t.lch();return n[1]+=j.Kn*e,n[1]<0&&(n[1]=0),new O(n,`lch`).alpha(t.alpha(),!0)},O.prototype.desaturate=function(e=1){return this.saturate(-e)},O.prototype.set=function(e,t,n=!1){let[r,i]=e.split(`.`),a=this[r]();if(i){let e=r.indexOf(i)-(r.substr(0,2)===`ok`?2:0);if(e>-1){if(h(t)==`string`)switch(t.charAt(0)){case`+`:a[e]+=+t;break;case`-`:a[e]+=+t;break;case`*`:a[e]*=+t.substr(1);break;case`/`:a[e]/=+t.substr(1);break;default:a[e]=+t}else if(h(t)===`number`)a[e]=t;else throw Error(`unsupported value for Color.set`);let i=new O(a,r);return n?(this._rgb=i._rgb,this):i}throw Error(`unknown channel ${i} in mode ${r}`)}else return a},O.prototype.tint=function(e=.5,...t){return F(this,`white`,e,...t)},O.prototype.shade=function(e=.5,...t){return F(this,`black`,e,...t)},P.rgb=(e,t,n)=>{let r=e._rgb,i=t._rgb;return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`rgb`)};var{sqrt:je,pow:Me}=Math;P.lrgb=(e,t,n)=>{let[r,i,a]=e._rgb,[o,s,c]=t._rgb;return new O(je(Me(r,2)*(1-n)+Me(o,2)*n),je(Me(i,2)*(1-n)+Me(s,2)*n),je(Me(a,2)*(1-n)+Me(c,2)*n),`rgb`)},P.lab=(e,t,n)=>{let r=e.lab(),i=t.lab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`lab`)};var Ne=(e,t,n,r)=>{let i,a;r===`hsl`?(i=e.hsl(),a=t.hsl()):r===`hsv`?(i=e.hsv(),a=t.hsv()):r===`hcg`?(i=e.hcg(),a=t.hcg()):r===`hsi`?(i=e.hsi(),a=t.hsi()):r===`lch`||r===`hcl`?(r=`hcl`,i=e.hcl(),a=t.hcl()):r===`oklch`&&(i=e.oklch().reverse(),a=t.oklch().reverse());let o,s,c,l,u,d;(r.substr(0,1)===`h`||r===`oklch`)&&([o,c,u]=i,[s,l,d]=a);let f,p,m,h;return!isNaN(o)&&!isNaN(s)?(h=s>o&&s-o>180?s-(o+360):s180?s+360-o:s-o,p=o+n*h):isNaN(o)?isNaN(s)?p=NaN:(p=s,(u==1||u==0)&&r!=`hsv`&&(f=l)):(p=o,(d==1||d==0)&&r!=`hsv`&&(f=c)),f===void 0&&(f=c+n*(l-c)),m=u+n*(d-u),r===`oklch`?new O([m,f,p],r):new O([p,f,m],r)},Pe=(e,t,n)=>Ne(e,t,n,`lch`);P.lch=Pe,P.hcl=Pe;var Fe=e=>{if(h(e)==`number`&&e>=0&&e<=16777215)return[e>>16,e>>8&255,e&255,1];throw Error(`unknown num color: `+e)},Ie=(...e)=>{let[t,n,r]=g(e,`rgb`);return(t<<16)+(n<<8)+r};O.prototype.num=function(){return Ie(this._rgb)},Object.assign(k,{num:(...e)=>new O(...e,`num`)}),D.format.num=Fe,D.autodetect.push({p:5,test:(...e)=>{if(e.length===1&&h(e[0])===`number`&&e[0]>=0&&e[0]<=16777215)return`num`}}),P.num=(e,t,n)=>{let r=e.num();return new O(r+n*(t.num()-r),`num`)};var{floor:Le}=Math,Re=(...e)=>{e=g(e,`hcg`);let[t,n,r]=e,i,a,o;r*=255;let s=n*255;if(n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Le(t),c=t-e,l=r*(1-n),u=l+s*(1-c),d=l+s*c,f=l+s;switch(e){case 0:[i,a,o]=[f,d,l];break;case 1:[i,a,o]=[u,f,l];break;case 2:[i,a,o]=[l,f,d];break;case 3:[i,a,o]=[l,u,f];break;case 4:[i,a,o]=[d,l,f];break;case 5:[i,a,o]=[f,l,u];break}}return[i,a,o,e.length>3?e[3]:1]},ze=(...e)=>{let[t,n,r]=g(e,`rgb`),i=y(t,n,r),a=b(t,n,r),o=a-i,s=o*100/255,c=i/(255-o)*100,l;return o===0?l=NaN:(t===a&&(l=(n-r)/o),n===a&&(l=2+(r-t)/o),r===a&&(l=4+(t-n)/o),l*=60,l<0&&(l+=360)),[l,s,c]};O.prototype.hcg=function(){return ze(this._rgb)},k.hcg=(...e)=>new O(...e,`hcg`),D.format.hcg=Re,D.autodetect.push({p:1,test:(...e)=>{if(e=g(e,`hcg`),h(e)===`array`&&e.length===3)return`hcg`}}),P.hcg=(e,t,n)=>Ne(e,t,n,`hcg`);var{cos:Be}=Math,Ve=(...e)=>{e=g(e,`hsi`);let[t,n,r]=e,i,a,o;return isNaN(t)&&(t=0),isNaN(n)&&(n=0),t>360&&(t-=360),t<0&&(t+=360),t/=360,t<1/3?(o=(1-n)/3,i=(1+n*Be(C*t)/Be(w-C*t))/3,a=1-(o+i)):t<2/3?(t-=1/3,i=(1-n)/3,a=(1+n*Be(C*t)/Be(w-C*t))/3,o=1-(i+a)):(t-=2/3,a=(1-n)/3,o=(1+n*Be(C*t)/Be(w-C*t))/3,i=1-(a+o)),i=f(r*i*3),a=f(r*a*3),o=f(r*o*3),[i*255,a*255,o*255,e.length>3?e[3]:1]},{min:He,sqrt:Ue,acos:We}=Math,Ge=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i,a=He(t,n,r),o=(t+n+r)/3,s=o>0?1-a/o:0;return s===0?i=NaN:(i=(t-n+(t-r))/2,i/=Ue((t-n)*(t-n)+(t-r)*(n-r)),i=We(i),r>n&&(i=C-i),i/=C),[i*360,s,o]};O.prototype.hsi=function(){return Ge(this._rgb)},k.hsi=(...e)=>new O(...e,`hsi`),D.format.hsi=Ve,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsi`),h(e)===`array`&&e.length===3)return`hsi`}}),P.hsi=(e,t,n)=>Ne(e,t,n,`hsi`);var Ke=(...e)=>{e=g(e,`hsl`);let[t,n,r]=e,i,a,o;if(n===0)i=a=o=r*255;else{let e=[0,0,0],s=[0,0,0],c=r<.5?r*(1+n):r+n-r*n,l=2*r-c,u=t/360;e[0]=u+1/3,e[1]=u,e[2]=u-1/3;for(let t=0;t<3;t++)e[t]<0&&(e[t]+=1),e[t]>1&&--e[t],6*e[t]<1?s[t]=l+(c-l)*6*e[t]:2*e[t]<1?s[t]=c:3*e[t]<2?s[t]=l+(c-l)*(2/3-e[t])*6:s[t]=l;[i,a,o]=[s[0]*255,s[1]*255,s[2]*255]}return e.length>3?[i,a,o,e[3]]:[i,a,o,1]},qe=(...e)=>{e=g(e,`rgba`);let[t,n,r]=e;t/=255,n/=255,r/=255;let i=y(t,n,r),a=b(t,n,r),o=(a+i)/2,s,c;return a===i?(s=0,c=NaN):s=o<.5?(a-i)/(a+i):(a-i)/(2-a-i),t==a?c=(n-r)/(a-i):n==a?c=2+(r-t)/(a-i):r==a&&(c=4+(t-n)/(a-i)),c*=60,c<0&&(c+=360),e.length>3&&e[3]!==void 0?[c,s,o,e[3]]:[c,s,o]};O.prototype.hsl=function(){return qe(this._rgb)},k.hsl=(...e)=>new O(...e,`hsl`),D.format.hsl=Ke,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsl`),h(e)===`array`&&e.length===3)return`hsl`}}),P.hsl=(e,t,n)=>Ne(e,t,n,`hsl`);var{floor:Je}=Math,Ye=(...e)=>{e=g(e,`hsv`);let[t,n,r]=e,i,a,o;if(r*=255,n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Je(t),s=t-e,c=r*(1-n),l=r*(1-n*s),u=r*(1-n*(1-s));switch(e){case 0:[i,a,o]=[r,u,c];break;case 1:[i,a,o]=[l,r,c];break;case 2:[i,a,o]=[c,r,u];break;case 3:[i,a,o]=[c,l,r];break;case 4:[i,a,o]=[u,c,r];break;case 5:[i,a,o]=[r,c,l];break}}return[i,a,o,e.length>3?e[3]:1]},{min:Xe,max:Ze}=Math,Qe=(...e)=>{e=g(e,`rgb`);let[t,n,r]=e,i=Xe(t,n,r),a=Ze(t,n,r),o=a-i,s,c,l;return l=a/255,a===0?(s=NaN,c=0):(c=o/a,t===a&&(s=(n-r)/o),n===a&&(s=2+(r-t)/o),r===a&&(s=4+(t-n)/o),s*=60,s<0&&(s+=360)),[s,c,l]};O.prototype.hsv=function(){return Qe(this._rgb)},k.hsv=(...e)=>new O(...e,`hsv`),D.format.hsv=Ye,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsv`),h(e)===`array`&&e.length===3)return`hsv`}}),P.hsv=(e,t,n)=>Ne(e,t,n,`hsv`);function $e(e,t){let n=e.length;Array.isArray(e[0])||(e=[e]),Array.isArray(t[0])||(t=t.map(e=>[e]));let r=t[0].length,i=t[0].map((e,n)=>t.map(e=>e[n])),a=e.map(e=>i.map(t=>Array.isArray(e)?e.reduce((e,n,r)=>e+n*(t[r]||0),0):t.reduce((t,n)=>t+n*e,0)));return n===1&&(a=a[0]),r===1?a.map(e=>e[0]):a}var et=(...e)=>{e=g(e,`lab`);let[t,n,r,...i]=e,[a,o,s]=tt([t,n,r]),[c,l,u]=de(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function tt(e){return $e([[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],$e([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],e).map(e=>e**3))}var nt=(...e)=>{let[t,n,r,...i]=g(e,`rgb`);return[...rt(he(t,n,r)),...i.length>0&&i[0]<1?[i[0]]:[]]};function rt(e){return $e([[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],$e([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],e).map(e=>Math.cbrt(e)))}O.prototype.oklab=function(){return nt(this._rgb)},Object.assign(k,{oklab:(...e)=>new O(...e,`oklab`)}),D.format.oklab=et,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklab`),h(e)===`array`&&e.length===3)return`oklab`}}),P.oklab=(e,t,n)=>{let r=e.oklab(),i=t.oklab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`oklab`)},P.oklch=(e,t,n)=>Ne(e,t,n,`oklch`);var{pow:it,sqrt:at,PI:ot,cos:st,sin:ct,atan2:lt}=Math,ut=(e,t=`lrgb`,n=null)=>{let r=e.length;n||=Array.from(Array(r)).map(()=>1);let i=r/n.reduce(function(e,t){return e+t});if(n.forEach((e,t)=>{n[t]*=i}),e=e.map(e=>new O(e)),t===`lrgb`)return dt(e,n);let a=e.shift(),o=a.get(t),s=[],c=0,l=0;for(let e=0;e{let i=e.get(t);u+=e.alpha()*n[r+1];for(let e=0;e=360;)t-=360;o[e]=t}else o[e]=o[e]/s[e];return u/=r,new O(o,t).alpha(u>.99999?1:u,!0)},dt=(e,t)=>{let n=e.length,r=[0,0,0,0];for(let i=0;i.9999999&&(r[3]=1),new O(p(r))},{pow:ft}=Math;function pt(e){let t=`rgb`,n=k(`#ccc`),r=0,i=[0,1],a=[0,1],o=[],s=[0,0],c=!1,l=[],u=!1,d=0,p=1,m=!1,g={},_=!0,v=1,y=function(e){if(e||=[`#fff`,`#000`],e&&h(e)===`string`&&k.brewer&&k.brewer[e.toLowerCase()]&&(e=k.brewer[e.toLowerCase()]),h(e)===`array`){e.length===1&&(e=[e[0],e[0]]),e=e.slice(0);for(let t=0;t=c[n];)n++;return n-1}return 0},x=e=>e,S=e=>e,C=function(e,r){let i,a;if(r??=!1,isNaN(e)||e===null)return n;a=r?e:c&&c.length>2?b(e)/(c.length-2):p===d?1:(e-d)/(p-d),a=S(a),r||(a=x(a)),v!==1&&(a=ft(a,v)),a=s[0]+a*(1-s[0]-s[1]),a=f(a,0,1);let u=Math.floor(a*1e4);if(_&&g[u])i=g[u];else{if(h(l)===`array`)for(let e=0;e=n&&e===o.length-1){i=l[e];break}if(a>n&&ag={};y(e);let T=function(e){let t=k(C(e));return u&&t[u]?t[u]():t};return T.classes=function(e){if(e!=null){if(h(e)===`array`)c=e,i=[e[0],e[e.length-1]];else{let t=k.analyze(i);c=e===0?[t.min,t.max]:k.limits(t,`e`,e)}return T}return c},T.domain=function(e){if(!arguments.length)return a;a=e.slice(0),d=e[0],p=e[e.length-1],o=[];let t=l.length;if(e.length===t&&d!==p)for(let t of Array.from(e))o.push((t-d)/(p-d));else{for(let e=0;e2){let t=e.map((t,n)=>n/(e.length-1)),n=e.map(e=>(e-d)/(p-d));n.every((e,n)=>t[n]===e)||(S=e=>{if(e<=0||e>=1)return e;let r=0;for(;e>=n[r+1];)r++;let i=(e-n[r])/(n[r+1]-n[r]);return t[r]+i*(t[r+1]-t[r])})}}return i=[d,p],T},T.mode=function(e){return arguments.length?(t=e,w(),T):t},T.range=function(e,t){return y(e,t),T},T.out=function(e){return u=e,T},T.spread=function(e){return arguments.length?(r=e,T):r},T.correctLightness=function(e){return e??=!0,m=e,w(),x=m?function(e){let t=C(0,!0).lab()[0],n=C(1,!0).lab()[0],r=t>n,i=C(e,!0).lab()[0],a=t+(n-t)*e,o=i-a,s=0,c=1,l=20;for(;Math.abs(o)>.01&&l-- >0;)(function(){return r&&(o*=-1),o<0?(s=e,e+=(c-e)*.5):(c=e,e+=(s-e)*.5),i=C(e,!0).lab()[0],o=i-a})();return e}:e=>e,T},T.padding=function(e){return e==null?s:(h(e)===`number`&&(e=[e,e]),s=e,T)},T.colors=function(t,n){arguments.length<2&&(n=`hex`);let r=[];if(arguments.length===0)r=l.slice(0);else if(t===1)r=[T(.5)];else if(t>1){let e=i[0],n=i[1]-e;r=mt(0,t,!1).map(r=>T(e+r/(t-1)*n))}else{e=[];let t=[];if(c&&c.length>2)for(let e=1,n=c.length,r=1<=n;r?en;r?e++:e--)t.push((c[e-1]+c[e])*.5);else t=i;r=t.map(e=>T(e))}return k[n]&&(r=r.map(e=>e[n]())),r},T.cache=function(e){return e==null?_:(_=e,T)},T.gamma=function(e){return e==null?v:(v=e,T)},T.nodata=function(e){return e==null?n:(n=k(e),T)},T}function mt(e,t,n){let r=[],i=ea;i?t++:t--)r.push(t);return r}var ht=function(e){let t=[1,1];for(let n=1;nnew O(e)),e.length===2)[n,r]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>n[t]+e*(r[t]-n[t])),`lab`)};else if(e.length===3)[n,r,i]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*n[t]+2*(1-e)*e*r[t]+e*e*i[t]),`lab`)};else if(e.length===4){let a;[n,r,i,a]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*(1-e)*n[t]+3*(1-e)*(1-e)*e*r[t]+3*(1-e)*e*e*i[t]+e*e*e*a[t]),`lab`)}}else if(e.length>=5){let n,r,i;n=e.map(e=>e.lab()),i=e.length-1,r=ht(i),t=function(e){let t=1-e;return new O([0,1,2].map(a=>n.reduce((n,o,s)=>n+r[s]*t**(i-s)*e**s*o[a],0)),`lab`)}}else throw RangeError(`No point in running bezier with only one color.`);return t},_t=e=>{let t=gt(e);return t.scale=()=>pt(t),t},{round:vt}=Math;O.prototype.rgb=function(e=!0){return e===!1?this._rgb.slice(0,3):this._rgb.slice(0,3).map(vt)},O.prototype.rgba=function(e=!0){return this._rgb.slice(0,4).map((t,n)=>n<3?e===!1?t:vt(t):t)},Object.assign(k,{rgb:(...e)=>new O(...e,`rgb`)}),D.format.rgb=(...e)=>{let t=g(e,`rgba`);return t[3]===void 0&&(t[3]=1),t},D.autodetect.push({p:3,test:(...e)=>{if(e=g(e,`rgba`),h(e)===`array`&&(e.length===3||e.length===4&&h(e[3])==`number`&&e[3]>=0&&e[3]<=1))return`rgb`}});var I=(e,t,n)=>{if(!I[n])throw Error(`unknown blend mode `+n);return I[n](e,t)},L=e=>(t,n)=>{let r=k(n).rgb(),i=k(t).rgb();return k.rgb(e(r,i))},R=e=>(t,n)=>{let r=[];return r[0]=e(t[0],n[0]),r[1]=e(t[1],n[1]),r[2]=e(t[2],n[2]),r};I.normal=L(R(e=>e)),I.multiply=L(R((e,t)=>e*t/255)),I.screen=L(R((e,t)=>255*(1-(1-e/255)*(1-t/255)))),I.overlay=L(R((e,t)=>t<128?2*e*t/255:255*(1-2*(1-e/255)*(1-t/255)))),I.darken=L(R((e,t)=>e>t?t:e)),I.lighten=L(R((e,t)=>e>t?e:t)),I.dodge=L(R((e,t)=>e===255?255:(e=t/255*255/(1-e/255),e>255?255:e))),I.burn=L(R((e,t)=>255*(1-(1-t/255)/(e/255))));var{pow:yt,sin:bt,cos:xt}=Math;function St(e=300,t=-1.5,n=1,r=1,i=[0,1]){let a=0,o;h(i)===`array`?o=i[1]-i[0]:(o=0,i=[i,i]);let s=function(s){let c=C*((e+120)/360+t*s),l=yt(i[0]+o*s,r),u=(a===0?n:n[0]+s*a)*l*(1-l)/2,d=xt(c),f=bt(c),m=l+u*(-.14861*d+1.78277*f),h=l+u*(-.29227*d-.90649*f),g=l+1.97294*d*u;return k(p([m*255,h*255,g*255,1]))};return s.start=function(t){return t==null?e:(e=t,s)},s.rotations=function(e){return e==null?t:(t=e,s)},s.gamma=function(e){return e==null?r:(r=e,s)},s.hue=function(e){return e==null?n:(n=e,h(n)===`array`?(a=n[1]-n[0],a===0&&(n=n[1])):a=0,s)},s.lightness=function(e){return e==null?i:(h(e)===`array`?(i=e,o=e[1]-e[0]):(i=[e,e],o=0),s)},s.scale=()=>k.scale(s),s.hue(n),s}var Ct=`0123456789abcdef`,{floor:wt,random:Tt}=Math,Et=(e=Tt)=>{let t=`#`;for(let n=0;n<6;n++)t+=Ct.charAt(wt(e()*16));return new O(t,`hex`)},{log:Dt,pow:Ot,floor:kt,abs:At}=Math;function jt(e,t=null){let n={min:Number.MAX_VALUE,max:Number.MAX_VALUE*-1,sum:0,values:[],count:0};return h(e)===`object`&&(e=Object.values(e)),e.forEach(e=>{t&&h(e)===`object`&&(e=e[t]),e!=null&&!isNaN(e)&&(n.values.push(e),n.sum+=e,en.max&&(n.max=e),n.count+=1)}),n.domain=[n.min,n.max],n.limits=(e,t)=>Mt(n,e,t),n}function Mt(e,t=`equal`,n=7){h(e)==`array`&&(e=jt(e));let{min:r,max:i}=e,a=e.values.sort((e,t)=>e-t);if(n===1)return[r,i];let o=[];if(t.substr(0,1)===`c`&&(o.push(r),o.push(i)),t.substr(0,1)===`e`){o.push(r);for(let e=1;e 0`);let e=Math.LOG10E*Dt(r),t=Math.LOG10E*Dt(i);o.push(r);for(let r=1;r200&&(l=!1)}let f={};for(let e=0;ee-t),o.push(p[0]);for(let e=1;e{e=new O(e),t=new O(t);let n=e.luminance(),r=t.luminance();return n>r?(n+.05)/(r+.05):(r+.05)/(n+.05)},Pt=.027,Ft=5e-4,It=.1,Lt=1.14,Rt=.022,zt=1.414,Bt=(e,t)=>{e=new O(e),t=new O(t),e.alpha()<1&&(e=F(t,e,e.alpha(),`rgb`));let n=Vt(...e.rgb()),r=Vt(...t.rgb()),i=n>=Rt?n:n+(Rt-n)**+zt,a=r>=Rt?r:r+(Rt-r)**+zt,o=a**.56-i**.57,s=a**.65-i**.62,c=Math.abs(a-i)0?c-Pt:c+Pt)*100};function Vt(e,t,n){return .2126729*(e/255)**2.4+.7151522*(t/255)**2.4+.072175*(n/255)**2.4}var{sqrt:z,pow:B,min:Ht,max:Ut,atan2:Wt,abs:Gt,cos:Kt,sin:qt,exp:Jt,PI:Yt}=Math;function Xt(e,t,n=1,r=1,i=1){var a=function(e){return 360*e/(2*Yt)},o=function(e){return 2*Yt*e/360};e=new O(e),t=new O(t);let[s,c,l]=Array.from(e.lab()),[u,d,f]=Array.from(t.lab()),p=(s+u)/2,m=(z(B(c,2)+B(l,2))+z(B(d,2)+B(f,2)))/2,h=.5*(1-z(B(m,7)/(B(m,7)+B(25,7)))),g=c*(1+h),_=d*(1+h),v=z(B(g,2)+B(l,2)),y=z(B(_,2)+B(f,2)),b=(v+y)/2,x=a(Wt(l,g)),S=a(Wt(f,_)),C=x>=0?x:x+360,w=S>=0?S:S+360,T=Gt(C-w)>180?(C+w+360)/2:(C+w)/2,ee=1-.17*Kt(o(T-30))+.24*Kt(o(2*T))+.32*Kt(o(3*T+6))-.2*Kt(o(4*T-63)),E=w-C;E=Gt(E)<=180?E:w<=C?E+360:E-360,E=2*z(v*y)*qt(o(E)/2);let D=u-s,te=y-v,k=1+.015*B(p-50,2)/z(20+B(p-50,2)),A=1+.045*b,ne=1+.015*b*ee,re=30*Jt(-B((T-275)/25,2)),ie=-(2*z(B(b,7)/(B(b,7)+B(25,7))))*qt(2*o(re));return Ut(0,Ht(100,z(B(D/(n*k),2)+B(te/(r*A),2)+B(E/(i*ne),2)+ie*(te/(r*A))*(E/(i*ne)))))}function Zt(e,t,n=`lab`){e=new O(e),t=new O(t);let r=e.get(n),i=t.get(n),a=0;for(let e in r){let t=(r[e]||0)-(i[e]||0);a+=t*t}return Math.sqrt(a)}var Qt=(...e)=>{try{return new O(...e),!0}catch{return!1}},$t={cool(){return pt([k.hsl(180,1,.9),k.hsl(250,.7,.4)])},hot(){return pt([`#000`,`#f00`,`#ff0`,`#fff`],[0,.25,.75,1]).mode(`rgb`)}},en={OrRd:[`#fff7ec`,`#fee8c8`,`#fdd49e`,`#fdbb84`,`#fc8d59`,`#ef6548`,`#d7301f`,`#b30000`,`#7f0000`],PuBu:[`#fff7fb`,`#ece7f2`,`#d0d1e6`,`#a6bddb`,`#74a9cf`,`#3690c0`,`#0570b0`,`#045a8d`,`#023858`],BuPu:[`#f7fcfd`,`#e0ecf4`,`#bfd3e6`,`#9ebcda`,`#8c96c6`,`#8c6bb1`,`#88419d`,`#810f7c`,`#4d004b`],Oranges:[`#fff5eb`,`#fee6ce`,`#fdd0a2`,`#fdae6b`,`#fd8d3c`,`#f16913`,`#d94801`,`#a63603`,`#7f2704`],BuGn:[`#f7fcfd`,`#e5f5f9`,`#ccece6`,`#99d8c9`,`#66c2a4`,`#41ae76`,`#238b45`,`#006d2c`,`#00441b`],YlOrBr:[`#ffffe5`,`#fff7bc`,`#fee391`,`#fec44f`,`#fe9929`,`#ec7014`,`#cc4c02`,`#993404`,`#662506`],YlGn:[`#ffffe5`,`#f7fcb9`,`#d9f0a3`,`#addd8e`,`#78c679`,`#41ab5d`,`#238443`,`#006837`,`#004529`],Reds:[`#fff5f0`,`#fee0d2`,`#fcbba1`,`#fc9272`,`#fb6a4a`,`#ef3b2c`,`#cb181d`,`#a50f15`,`#67000d`],RdPu:[`#fff7f3`,`#fde0dd`,`#fcc5c0`,`#fa9fb5`,`#f768a1`,`#dd3497`,`#ae017e`,`#7a0177`,`#49006a`],Greens:[`#f7fcf5`,`#e5f5e0`,`#c7e9c0`,`#a1d99b`,`#74c476`,`#41ab5d`,`#238b45`,`#006d2c`,`#00441b`],YlGnBu:[`#ffffd9`,`#edf8b1`,`#c7e9b4`,`#7fcdbb`,`#41b6c4`,`#1d91c0`,`#225ea8`,`#253494`,`#081d58`],Purples:[`#fcfbfd`,`#efedf5`,`#dadaeb`,`#bcbddc`,`#9e9ac8`,`#807dba`,`#6a51a3`,`#54278f`,`#3f007d`],GnBu:[`#f7fcf0`,`#e0f3db`,`#ccebc5`,`#a8ddb5`,`#7bccc4`,`#4eb3d3`,`#2b8cbe`,`#0868ac`,`#084081`],Greys:[`#ffffff`,`#f0f0f0`,`#d9d9d9`,`#bdbdbd`,`#969696`,`#737373`,`#525252`,`#252525`,`#000000`],YlOrRd:[`#ffffcc`,`#ffeda0`,`#fed976`,`#feb24c`,`#fd8d3c`,`#fc4e2a`,`#e31a1c`,`#bd0026`,`#800026`],PuRd:[`#f7f4f9`,`#e7e1ef`,`#d4b9da`,`#c994c7`,`#df65b0`,`#e7298a`,`#ce1256`,`#980043`,`#67001f`],Blues:[`#f7fbff`,`#deebf7`,`#c6dbef`,`#9ecae1`,`#6baed6`,`#4292c6`,`#2171b5`,`#08519c`,`#08306b`],PuBuGn:[`#fff7fb`,`#ece2f0`,`#d0d1e6`,`#a6bddb`,`#67a9cf`,`#3690c0`,`#02818a`,`#016c59`,`#014636`],Viridis:[`#440154`,`#482777`,`#3f4a8a`,`#31678e`,`#26838f`,`#1f9d8a`,`#6cce5a`,`#b6de2b`,`#fee825`],Spectral:[`#9e0142`,`#d53e4f`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#e6f598`,`#abdda4`,`#66c2a5`,`#3288bd`,`#5e4fa2`],RdYlGn:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#d9ef8b`,`#a6d96a`,`#66bd63`,`#1a9850`,`#006837`],RdBu:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#f7f7f7`,`#d1e5f0`,`#92c5de`,`#4393c3`,`#2166ac`,`#053061`],PiYG:[`#8e0152`,`#c51b7d`,`#de77ae`,`#f1b6da`,`#fde0ef`,`#f7f7f7`,`#e6f5d0`,`#b8e186`,`#7fbc41`,`#4d9221`,`#276419`],PRGn:[`#40004b`,`#762a83`,`#9970ab`,`#c2a5cf`,`#e7d4e8`,`#f7f7f7`,`#d9f0d3`,`#a6dba0`,`#5aae61`,`#1b7837`,`#00441b`],RdYlBu:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee090`,`#ffffbf`,`#e0f3f8`,`#abd9e9`,`#74add1`,`#4575b4`,`#313695`],BrBG:[`#543005`,`#8c510a`,`#bf812d`,`#dfc27d`,`#f6e8c3`,`#f5f5f5`,`#c7eae5`,`#80cdc1`,`#35978f`,`#01665e`,`#003c30`],RdGy:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#ffffff`,`#e0e0e0`,`#bababa`,`#878787`,`#4d4d4d`,`#1a1a1a`],PuOr:[`#7f3b08`,`#b35806`,`#e08214`,`#fdb863`,`#fee0b6`,`#f7f7f7`,`#d8daeb`,`#b2abd2`,`#8073ac`,`#542788`,`#2d004b`],Set2:[`#66c2a5`,`#fc8d62`,`#8da0cb`,`#e78ac3`,`#a6d854`,`#ffd92f`,`#e5c494`,`#b3b3b3`],Accent:[`#7fc97f`,`#beaed4`,`#fdc086`,`#ffff99`,`#386cb0`,`#f0027f`,`#bf5b17`,`#666666`],Set1:[`#e41a1c`,`#377eb8`,`#4daf4a`,`#984ea3`,`#ff7f00`,`#ffff33`,`#a65628`,`#f781bf`,`#999999`],Set3:[`#8dd3c7`,`#ffffb3`,`#bebada`,`#fb8072`,`#80b1d3`,`#fdb462`,`#b3de69`,`#fccde5`,`#d9d9d9`,`#bc80bd`,`#ccebc5`,`#ffed6f`],Dark2:[`#1b9e77`,`#d95f02`,`#7570b3`,`#e7298a`,`#66a61e`,`#e6ab02`,`#a6761d`,`#666666`],Paired:[`#a6cee3`,`#1f78b4`,`#b2df8a`,`#33a02c`,`#fb9a99`,`#e31a1c`,`#fdbf6f`,`#ff7f00`,`#cab2d6`,`#6a3d9a`,`#ffff99`,`#b15928`],Pastel2:[`#b3e2cd`,`#fdcdac`,`#cbd5e8`,`#f4cae4`,`#e6f5c9`,`#fff2ae`,`#f1e2cc`,`#cccccc`],Pastel1:[`#fbb4ae`,`#b3cde3`,`#ccebc5`,`#decbe4`,`#fed9a6`,`#ffffcc`,`#e5d8bd`,`#fddaec`,`#f2f2f2`]},tn=Object.keys(en),nn=new Map(tn.map(e=>[e.toLowerCase(),e])),rn=typeof Proxy==`function`?new Proxy(en,{get(e,t){let n=t.toLowerCase();if(nn.has(n))return e[nn.get(n)]},getOwnPropertyNames(){return Object.getOwnPropertyNames(tn)}}):en,an=(...e)=>{e=g(e,`cmyk`);let[t,n,r,i]=e,a=e.length>4?e[4]:1;return i===1?[0,0,0,a]:[t>=1?0:255*(1-t)*(1-i),n>=1?0:255*(1-n)*(1-i),r>=1?0:255*(1-r)*(1-i),a]},{max:on}=Math,sn=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i=1-on(t,on(n,r)),a=i<1?1/(1-i):0;return[(1-t-i)*a,(1-n-i)*a,(1-r-i)*a,i]};O.prototype.cmyk=function(){return sn(this._rgb)},Object.assign(k,{cmyk:(...e)=>new O(...e,`cmyk`)}),D.format.cmyk=an,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`cmyk`),h(e)===`array`&&e.length===4)return`cmyk`}});var cn=(...e)=>{let t=g(e,`hsla`),n=_(e)||`lsa`;return t[0]=x(t[0]||0)+`deg`,t[1]=x(t[1]*100)+`%`,t[2]=x(t[2]*100)+`%`,n===`hsla`||t.length>3&&t[3]<1?(t[3]=`/ `+(t.length>3?t[3]:1),n=`hsla`):t.length=3,`${n.substr(0,3)}(${t.join(` `)})`},ln=(...e)=>{let t=g(e,`lab`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=x(t[2]),n===`laba`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lab(${t.join(` `)})`},un=(...e)=>{let t=g(e,`lch`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,n===`lcha`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lch(${t.join(` `)})`},dn=(...e)=>{let t=g(e,`lab`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=S(t[2]),t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklab(${t.join(` `)})`},fn=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=nt(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},pn=(...e)=>{let t=g(e,`lch`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklch(${t.join(` `)})`},{round:mn}=Math,hn=(...e)=>{let t=g(e,`rgba`),n=_(e)||`rgb`;if(n.substr(0,3)===`hsl`)return cn(qe(t),n);if(n.substr(0,3)===`lab`){let e=ce();M(`d50`);let r=ln(fe(t),n);return M(e),r}if(n.substr(0,3)===`lch`){let e=ce();M(`d50`);let r=un(Ae(t),n);return M(e),r}return n.substr(0,5)===`oklab`?dn(nt(t)):n.substr(0,5)===`oklch`?pn(fn(t)):(t[0]=mn(t[0]),t[1]=mn(t[1]),t[2]=mn(t[2]),(n===`rgba`||t.length>3&&t[3]<1)&&(t[3]=`/ `+(t.length>3?t[3]:1),n=`rgba`),`${n.substr(0,3)}(${t.slice(0,n===`rgb`?3:4).join(` `)})`)},gn=(...e)=>{e=g(e,`lch`);let[t,n,r,...i]=e,[a,o,s]=Ce(t,n,r),[c,l,u]=et(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},V=`((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)`,H=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)`,_n=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)`,U=`\\s*`,vn=`\\s+`,yn=`\\s*,\\s*`,bn=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)`,xn=`\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?`,Sn=RegExp(`^rgba?\\(`+U+[V,V,V].join(vn)+xn+`\\)$`),Cn=RegExp(`^rgb\\(`+U+[V,V,V].join(yn)+U+`\\)$`),wn=RegExp(`^rgba\\(`+U+[V,V,V,H].join(yn)+U+`\\)$`),Tn=RegExp(`^hsla?\\(`+U+[bn,_n,_n].join(vn)+xn+`\\)$`),En=RegExp(`^hsl?\\(`+U+[bn,_n,_n].join(yn)+U+`\\)$`),Dn=/^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,On=RegExp(`^lab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),kn=RegExp(`^lch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),An=RegExp(`^oklab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),jn=RegExp(`^oklch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),{round:Mn}=Math,Nn=e=>e.map((e,t)=>t<=2?f(Mn(e),0,255):e),W=(e,t=0,n=100,r=!1)=>(typeof e==`string`&&e.endsWith(`%`)&&(e=parseFloat(e.substring(0,e.length-1))/100,e=r?t+(e+1)*.5*(n-t):t+e*(n-t)),+e),G=(e,t)=>e===`none`?t:e,Pn=e=>{if(e=e.toLowerCase().trim(),e===`transparent`)return[0,0,0,0];let t;if(D.format.named)try{return D.format.named(e)}catch{}if((t=e.match(Sn))||(t=e.match(Cn))){let e=t.slice(1,4);for(let t=0;t<3;t++)e[t]=+W(G(e[t],0),0,255);e=Nn(e);let n=t[4]===void 0?1:+W(t[4],0,1);return e[3]=n,e}if(t=e.match(wn)){let e=t.slice(1,5);for(let t=0;t<4;t++)e[t]=+W(e[t],0,255);return e}if((t=e.match(Tn))||(t=e.match(En))){let e=t.slice(1,4);e[0]=+G(e[0].replace(`deg`,``),0),e[1]=W(G(e[1],0),0,100)*.01,e[2]=W(G(e[2],0),0,100)*.01;let n=Nn(Ke(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(Dn)){let e=t.slice(1,4);e[1]*=.01,e[2]*=.01;let n=Ke(e);for(let e=0;e<3;e++)n[e]=Mn(n[e]);return n[3]=+t[4],n}if(t=e.match(On)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,100),e[1]=W(G(e[1],0),-125,125,!0),e[2]=W(G(e[2],0),-125,125,!0);let n=ce();M(`d50`);let r=Nn(N(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(kn)){let e=t.slice(1,4);e[0]=W(e[0],0,100),e[1]=W(G(e[1],0),0,150,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=ce();M(`d50`);let r=Nn(we(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(An)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),-.4,.4,!0),e[2]=W(G(e[2],0),-.4,.4,!0);let n=Nn(et(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(jn)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),0,.4,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=Nn(gn(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}};Pn.test=e=>Sn.test(e)||Tn.test(e)||On.test(e)||kn.test(e)||An.test(e)||jn.test(e)||Cn.test(e)||wn.test(e)||En.test(e)||Dn.test(e)||e===`transparent`,O.prototype.css=function(e){return hn(this._rgb,e)},k.css=(...e)=>new O(...e,`css`),D.format.css=Pn,D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&Pn.test(e))return`css`}}),D.format.gl=(...e)=>{let t=g(e,`rgba`);return t[0]*=255,t[1]*=255,t[2]*=255,t},k.gl=(...e)=>new O(...e,`gl`),O.prototype.gl=function(){let e=this._rgb;return[e[0]/255,e[1]/255,e[2]/255,e[3]]},O.prototype.hex=function(e){return oe(this._rgb,e)},k.hex=(...e)=>new O(...e,`hex`),D.format.hex=ie,D.autodetect.push({p:4,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&[3,4,5,6,7,8,9].indexOf(e.length)>=0)return`hex`}});var{log:Fn}=Math,In=e=>{let t=e/100,n,r,i;return t<66?(n=255,r=t<6?0:-155.25485562709179-.44596950469579133*(r=t-2)+104.49216199393888*Fn(r),i=t<20?0:-254.76935184120902+.8274096064007395*(i=t-10)+115.67994401066147*Fn(i)):(n=351.97690566805693+.114206453784165*(n=t-55)-40.25366309332127*Fn(n),r=325.4494125711974+.07943456536662342*(r=t-50)-28.0852963507957*Fn(r),i=255),[n,r,i,1]},{round:Ln}=Math,Rn=(...e)=>{let t=g(e,`rgb`),n=t[0],r=t[2],i=1e3,a=4e4,o;for(;a-i>.4;){o=(a+i)*.5;let e=In(o);e[2]/e[0]>=r/n?a=o:i=o}return Ln(o)};O.prototype.temp=O.prototype.kelvin=O.prototype.temperature=function(){return Rn(this._rgb)};var zn=(...e)=>new O(...e,`temp`);Object.assign(k,{temp:zn,kelvin:zn,temperature:zn}),D.format.temp=D.format.kelvin=D.format.temperature=In,O.prototype.oklch=function(){return fn(this._rgb)},Object.assign(k,{oklch:(...e)=>new O(...e,`oklch`)}),D.format.oklch=gn,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklch`),h(e)===`array`&&e.length===3)return`oklch`}}),Object.assign(k,{analyze:jt,average:ut,bezier:_t,blend:I,brewer:rn,Color:O,colors:A,contrast:Nt,contrastAPCA:Bt,cubehelix:St,deltaE:Xt,distance:Zt,input:D,interpolate:F,limits:Mt,mix:F,random:Et,scale:pt,scales:$t,valid:Qt});var K=k,q=class e{constructor(){this.hex=`#000000`,this.rgb_r=0,this.rgb_g=0,this.rgb_b=0,this.xyz_x=0,this.xyz_y=0,this.xyz_z=0,this.luv_l=0,this.luv_u=0,this.luv_v=0,this.lch_l=0,this.lch_c=0,this.lch_h=0,this.hsluv_h=0,this.hsluv_s=0,this.hsluv_l=0,this.hpluv_h=0,this.hpluv_p=0,this.hpluv_l=0,this.r0s=0,this.r0i=0,this.r1s=0,this.r1i=0,this.g0s=0,this.g0i=0,this.g1s=0,this.g1i=0,this.b0s=0,this.b0i=0,this.b1s=0,this.b1i=0}static fromLinear(e){return e<=.0031308?12.92*e:1.055*e**(1/2.4)-.055}static toLinear(e){return e>.04045?((e+.055)/1.055)**2.4:e/12.92}static yToL(t){return t<=e.epsilon?t/e.refY*e.kappa:116*(t/e.refY)**(1/3)-16}static lToY(t){return t<=8?e.refY*t/e.kappa:e.refY*((t+16)/116)**3}static rgbChannelToHex(t){let n=Math.round(t*255),r=n%16,i=(n-r)/16|0;return e.hexChars.charAt(i)+e.hexChars.charAt(r)}static hexToRgbChannel(t,n){let r=e.hexChars.indexOf(t.charAt(n)),i=e.hexChars.indexOf(t.charAt(n+1));return(r*16+i)/255}static distanceFromOriginAngle(e,t,n){let r=t/(Math.sin(n)-e*Math.cos(n));return r<0?1/0:r}static distanceFromOrigin(e,t){return Math.abs(t)/Math.sqrt(e**2+1)}static min6(e,t,n,r,i,a){return Math.min(e,Math.min(t,Math.min(n,Math.min(r,Math.min(i,a)))))}rgbToHex(){this.hex=`#`,this.hex+=e.rgbChannelToHex(this.rgb_r),this.hex+=e.rgbChannelToHex(this.rgb_g),this.hex+=e.rgbChannelToHex(this.rgb_b)}hexToRgb(){this.hex=this.hex.toLowerCase(),this.rgb_r=e.hexToRgbChannel(this.hex,1),this.rgb_g=e.hexToRgbChannel(this.hex,3),this.rgb_b=e.hexToRgbChannel(this.hex,5)}xyzToRgb(){this.rgb_r=e.fromLinear(e.m_r0*this.xyz_x+e.m_r1*this.xyz_y+e.m_r2*this.xyz_z),this.rgb_g=e.fromLinear(e.m_g0*this.xyz_x+e.m_g1*this.xyz_y+e.m_g2*this.xyz_z),this.rgb_b=e.fromLinear(e.m_b0*this.xyz_x+e.m_b1*this.xyz_y+e.m_b2*this.xyz_z)}rgbToXyz(){let t=e.toLinear(this.rgb_r),n=e.toLinear(this.rgb_g),r=e.toLinear(this.rgb_b);this.xyz_x=.41239079926595*t+.35758433938387*n+.18048078840183*r,this.xyz_y=.21263900587151*t+.71516867876775*n+.072192315360733*r,this.xyz_z=.019330818715591*t+.11919477979462*n+.95053215224966*r}xyzToLuv(){let t=this.xyz_x+15*this.xyz_y+3*this.xyz_z,n=4*this.xyz_x,r=9*this.xyz_y;t===0?(n=NaN,r=NaN):(n/=t,r/=t),this.luv_l=e.yToL(this.xyz_y),this.luv_l===0?(this.luv_u=0,this.luv_v=0):(this.luv_u=13*this.luv_l*(n-e.refU),this.luv_v=13*this.luv_l*(r-e.refV))}luvToXyz(){if(this.luv_l===0){this.xyz_x=0,this.xyz_y=0,this.xyz_z=0;return}let t=this.luv_u/(13*this.luv_l)+e.refU,n=this.luv_v/(13*this.luv_l)+e.refV;this.xyz_y=e.lToY(this.luv_l),this.xyz_x=0-9*this.xyz_y*t/((t-4)*n-t*n),this.xyz_z=(9*this.xyz_y-15*n*this.xyz_y-n*this.xyz_x)/(3*n)}luvToLch(){if(this.lch_l=this.luv_l,this.lch_c=Math.sqrt(this.luv_u*this.luv_u+this.luv_v*this.luv_v),this.lch_c<1e-8)this.lch_h=0;else{let e=Math.atan2(this.luv_v,this.luv_u);this.lch_h=e*180/Math.PI,this.lch_h<0&&(this.lch_h=360+this.lch_h)}}lchToLuv(){let e=this.lch_h/180*Math.PI;this.luv_l=this.lch_l,this.luv_u=Math.cos(e)*this.lch_c,this.luv_v=Math.sin(e)*this.lch_c}calculateBoundingLines(t){let n=(t+16)**3/1560896,r=n>e.epsilon?n:t/e.kappa,i=r*(284517*e.m_r0-94839*e.m_r2),a=r*(838422*e.m_r2+769860*e.m_r1+731718*e.m_r0),o=r*(632260*e.m_r2-126452*e.m_r1),s=r*(284517*e.m_g0-94839*e.m_g2),c=r*(838422*e.m_g2+769860*e.m_g1+731718*e.m_g0),l=r*(632260*e.m_g2-126452*e.m_g1),u=r*(284517*e.m_b0-94839*e.m_b2),d=r*(838422*e.m_b2+769860*e.m_b1+731718*e.m_b0),f=r*(632260*e.m_b2-126452*e.m_b1);this.r0s=i/o,this.r0i=a*t/o,this.r1s=i/(o+126452),this.r1i=(a-769860)*t/(o+126452),this.g0s=s/l,this.g0i=c*t/l,this.g1s=s/(l+126452),this.g1i=(c-769860)*t/(l+126452),this.b0s=u/f,this.b0i=d*t/f,this.b1s=u/(f+126452),this.b1i=(d-769860)*t/(f+126452)}calcMaxChromaHpluv(){let t=e.distanceFromOrigin(this.r0s,this.r0i),n=e.distanceFromOrigin(this.r1s,this.r1i),r=e.distanceFromOrigin(this.g0s,this.g0i),i=e.distanceFromOrigin(this.g1s,this.g1i),a=e.distanceFromOrigin(this.b0s,this.b0i),o=e.distanceFromOrigin(this.b1s,this.b1i);return e.min6(t,n,r,i,a,o)}calcMaxChromaHsluv(t){let n=t/360*Math.PI*2,r=e.distanceFromOriginAngle(this.r0s,this.r0i,n),i=e.distanceFromOriginAngle(this.r1s,this.r1i,n),a=e.distanceFromOriginAngle(this.g0s,this.g0i,n),o=e.distanceFromOriginAngle(this.g1s,this.g1i,n),s=e.distanceFromOriginAngle(this.b0s,this.b0i,n),c=e.distanceFromOriginAngle(this.b1s,this.b1i,n);return e.min6(r,i,a,o,s,c)}hsluvToLch(){if(this.hsluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hsluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hsluv_l,this.calculateBoundingLines(this.hsluv_l);let e=this.calcMaxChromaHsluv(this.hsluv_h);this.lch_c=e/100*this.hsluv_s}this.lch_h=this.hsluv_h}lchToHsluv(){if(this.lch_l>99.9999999)this.hsluv_s=0,this.hsluv_l=100;else if(this.lch_l<1e-8)this.hsluv_s=0,this.hsluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHsluv(this.lch_h);this.hsluv_s=this.lch_c/e*100,this.hsluv_l=this.lch_l}this.hsluv_h=this.lch_h}hpluvToLch(){if(this.hpluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hpluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hpluv_l,this.calculateBoundingLines(this.hpluv_l);let e=this.calcMaxChromaHpluv();this.lch_c=e/100*this.hpluv_p}this.lch_h=this.hpluv_h}lchToHpluv(){if(this.lch_l>99.9999999)this.hpluv_p=0,this.hpluv_l=100;else if(this.lch_l<1e-8)this.hpluv_p=0,this.hpluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHpluv();this.hpluv_p=this.lch_c/e*100,this.hpluv_l=this.lch_l}this.hpluv_h=this.lch_h}hsluvToRgb(){this.hsluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hpluvToRgb(){this.hpluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hsluvToHex(){this.hsluvToRgb(),this.rgbToHex()}hpluvToHex(){this.hpluvToRgb(),this.rgbToHex()}rgbToHsluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHsluv()}rgbToHpluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHpluv()}hexToHsluv(){this.hexToRgb(),this.rgbToHsluv()}hexToHpluv(){this.hexToRgb(),this.rgbToHpluv()}};q.hexChars=`0123456789abcdef`,q.refY=1,q.refU=.19783000664283,q.refV=.46831999493879,q.kappa=903.2962962,q.epsilon=.0088564516,q.m_r0=3.240969941904521,q.m_r1=-1.537383177570093,q.m_r2=-.498610760293,q.m_g0=-.96924363628087,q.m_g1=1.87596750150772,q.m_g2=.041555057407175,q.m_b0=.055630079696993,q.m_b1=-.20397695888897,q.m_b2=1.056971514242878;var Bn=s(((e,t)=>{function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.exports=n})),Vn=s(((e,t)=>{var n=Bn(),r,i;function a(){for(var e in i=[`toString`,`toLocaleString`,`valueOf`,`hasOwnProperty`,`isPrototypeOf`,`propertyIsEnumerable`,`constructor`],r=!0,{toString:null})r=!1}function o(e,t,o){var c,l=0;for(c in r??a(),e)if(s(t,e,c,o)===!1)break;if(r)for(var u=e.constructor,d=!!u&&e===u.prototype;(c=i[l++])&&!((c!==`constructor`||!d&&n(e,c))&&e[c]!==Object.prototype[c]&&s(t,e,c,o)===!1););}function s(e,t,n,r){return e.call(r,t[n],n,t)}t.exports=o})),Hn=s(((e,t)=>{var n=Vn();function r(e){var t=[];return n(e,function(e,n){typeof e==`function`&&t.push(n)}),t.sort()}t.exports=r})),Un=s(((e,t)=>{function n(e,t,n){var r=e.length;t=t==null?0:t<0?Math.max(r+t,0):Math.min(t,r),n=n==null?r:n<0?Math.max(r+n,0):Math.min(n,r);for(var i=[];t{var n=Un();function r(e,t,r){var i=n(arguments,2);return function(){return e.apply(t,i.concat(n(arguments)))}}t.exports=r})),Gn=s(((e,t)=>{function n(e,t,n){if(e!=null)for(var r=-1,i=e.length;++r{var n=Hn(),r=Wn(),i=Gn(),a=Un();function o(e,t){i(arguments.length>1?a(arguments,1):n(e),function(t){e[t]=r(e[t],e)})}t.exports=o})),J=s(((e,t)=>{var n=Bn(),r=Vn();function i(e,t,i){r(e,function(r,a){if(n(e,a))return t.call(i,e[a],a,e)})}t.exports=i})),qn=s(((e,t)=>{function n(e){return e}t.exports=n})),Jn=s(((e,t)=>{function n(e){return function(t){return t[e]}}t.exports=n})),Yn=s(((e,t)=>{var n=/^\[object (.*)\]$/,r=Object.prototype.toString,i;function a(e){return e===null?`Null`:e===i?`Undefined`:n.exec(r.call(e))[1]}t.exports=a})),Xn=s(((e,t)=>{var n=Yn();function r(e,t){return n(e)===t}t.exports=r})),Zn=s(((e,t)=>{var n=Xn();t.exports=Array.isArray||function(e){return n(e,`Array`)}})),Qn=s(((e,t)=>{var n=J(),r=Zn();function i(e,t){for(var n=-1,r=e.length;++n{var n=qn(),r=Jn(),i=Qn();function a(e,t){if(e==null)return n;switch(typeof e){case`function`:return t===void 0?e:function(n,r,i){return e.call(t,n,r,i)};case`object`:return function(t){return i(t,e)};case`string`:case`number`:return r(e)}}t.exports=a})),$n=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!1;return n(e,function(n,r){if(t(n,r,e))return a=!0,!1}),a}t.exports=i})),er=s(((e,t)=>{var n=$n();function r(e,t){return n(e,function(e){return e===t})}t.exports=r})),tr=s(((e,t)=>{function n(e){return!!e&&typeof e==`object`&&e.constructor===Object}t.exports=n})),nr=s(((e,t)=>{var n=J(),r=tr();function i(e,t){for(var a=0,o=arguments.length,s;++a{var n=J(),r=tr();function i(e,t){for(var r=0,i=arguments.length,o;++r{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!0;return n(e,function(n,r){if(!t(n,r,e))return a=!1,!1}),a}t.exports=i})),ar=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Object`)}t.exports=r})),or=s(((e,t)=>{function n(e,t){return e===t?e!==0||1/e==1/t:e!==e&&t!==t}t.exports=n})),sr=s(((e,t)=>{var n=Bn(),r=ir(),i=ar(),a=or();function o(e){return function(t,r){return n(this,r)&&e(t,this[r])}}function s(e,t){return n(this,t)}function c(e,t,n){return n||=a,!i(e)||!i(t)?n(e,t):r(e,o(n),t)&&r(t,s,e)}t.exports=c})),cr=s(((e,t)=>{var n=Gn(),r=Un(),i=J();function a(e,t){return n(r(arguments,1),function(t){i(t,function(t,n){e[n]??(e[n]=t)})}),e}t.exports=a})),lr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){t(e,n,r)&&(a[n]=e)}),a}t.exports=i})),ur=s(((e,t)=>{var n=$n(),r=Y();function i(e,t,i){t=r(t,i);var a;return n(e,function(e,n,r){if(t(e,n,r))return a=e,!0}),a}t.exports=i})),dr=s(((e,t)=>{var n=J(),r=tr();function i(e,t,a,o){return n(e,function(e,n){var s=a?a+`.`+n:n;o!==0&&r(e)?i(e,t,s,o-1):t[s]=e}),t}function a(e,t){return e==null?{}:(t??=-1,i(e,{},``,t))}t.exports=a})),fr=s(((e,t)=>{function n(e){switch(typeof e){case`string`:case`number`:case`boolean`:return!0}return e==null}t.exports=n})),pr=s(((e,t)=>{fr();function n(e,t){for(var n=t.split(`.`),r=n.pop();t=n.shift();)if(e=e[t],e==null)return;return e[r]}t.exports=n})),mr=s(((e,t)=>{var n=pr(),r;function i(e,t){return n(e,t)!==r}t.exports=i})),hr=s(((e,t)=>{var n=J();t.exports=Object.keys||function(e){var t=[];return n(e,function(e,n){t.push(n)}),t}})),gr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){a[n]=t(e,n,r)}),a}t.exports=i})),_r=s(((e,t)=>{var n=J();function r(e,t){var r=!0;return n(t,function(t,n){if(e[n]!==t)return r=!1}),r}t.exports=r})),vr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return 1/0;if(e.length&&!t)return Math.max.apply(Math,e);t=n(t,r);for(var i,a=-1/0,o,s,c=-1,l=e.length;++ca&&(a=s,i=o);return i}t.exports=r})),yr=s(((e,t)=>{var n=J();function r(e){var t=[];return n(e,function(e,n){t.push(e)}),t}t.exports=r})),br=s(((e,t)=>{var n=vr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),xr=s(((e,t)=>{var n=J();function r(e,t){for(var r=0,a=arguments.length,o;++r{var n=Yn(),r=tr(),i=xr();function a(e){switch(n(e)){case`Object`:return o(e);case`Array`:return l(e);case`RegExp`:return s(e);case`Date`:return c(e);default:return e}}function o(e){return r(e)?i({},e):e}function s(e){var t=``;return t+=e.multiline?`m`:``,t+=e.global?`g`:``,t+=e.ignoreCase?`i`:``,new RegExp(e.source,t)}function c(e){return new Date(+e)}function l(e){return e.slice()}t.exports=a})),Cr=s(((e,t)=>{var n=Sr(),r=J(),i=Yn(),a=tr();function o(e,t){switch(i(e)){case`Object`:return s(e,t);case`Array`:return c(e,t);default:return n(e)}}function s(e,t){if(a(e)){var n={};return r(e,function(e,n){this[n]=o(e,t)},n),n}else if(t)return t(e);else return e}function c(e,t){for(var n=[],r=-1,i=e.length;++r{var n=Bn(),r=Cr(),i=ar();function a(){for(var e=1,t,o,s,c=r(arguments[0]);s=arguments[e++];)for(t in s)n(s,t)&&(o=s[t],i(o)&&i(c[t])?c[t]=a(c[t],o):c[t]=r(o));return c}t.exports=a})),Tr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return-1/0;if(e.length&&!t)return Math.min.apply(Math,e);t=n(t,r);for(var i,a=1/0,o,s,c=-1,l=e.length;++c{var n=Tr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),Dr=s(((e,t)=>{var n=Gn();function r(e,t){return t&&n(t.split(`.`),function(t){e[t]||(e[t]={}),e=e[t]}),e}t.exports=r})),Or=s(((e,t)=>{function n(e,t,n){if(n||=0,e==null)return-1;for(var r=e.length,i=n<0?r+n:n;i{var n=Or();function r(e,t){return n(e,t)!==-1}t.exports=r})),Ar=s(((e,t)=>{var n=Un(),r=kr();function i(e,t){var i=typeof arguments[1]==`string`?n(arguments,1):arguments[1],a={};for(var o in e)e.hasOwnProperty(o)&&!r(i,o)&&(a[o]=e[o]);return a}t.exports=i})),jr=s(((e,t)=>{var n=Un();function r(e,t){for(var r=typeof arguments[1]==`string`?n(arguments,1):arguments[1],i={},a=0,o;o=r[a++];)i[o]=e[o];return i}t.exports=r})),Mr=s(((e,t)=>{var n=gr(),r=Jn();function i(e,t){return n(e,r(t))}t.exports=i})),Nr=s(((e,t)=>{var n=J();function r(e){var t=0;return n(e,function(){t++}),t}t.exports=r})),Pr=s(((e,t)=>{var n=J(),r=Nr();function i(e,t,i,a){var o=arguments.length>2;if(!r(e)&&!o)throw Error(`reduce of empty object with no initial value`);return n(e,function(e,n,r){o?i=t.call(a,i,e,n,r):(i=e,o=!0)}),i}t.exports=i})),Fr=s(((e,t)=>{var n=lr(),r=Y();function i(e,t,i){return t=r(t,i),n(e,function(e,n,r){return!t(e,n,r)},i)}t.exports=i})),Ir=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Function`)}t.exports=r})),Lr=s(((e,t)=>{var n=Ir();function r(e,t){var r=e[t];if(r!==void 0)return n(r)?r.call(e):r}t.exports=r})),Rr=s(((e,t)=>{var n=Dr();function r(e,t,r){var i=/^(.+)\.(.+)$/.exec(t);i?n(e,i[1])[i[2]]=r:e[t]=r}t.exports=r})),zr=s(((e,t)=>{var n=mr();function r(e,t){if(n(e,t)){for(var r=t.split(`.`),i=r.pop();t=r.shift();)e=e[t];return delete e[i]}else return!0}t.exports=r})),Br=s(((e,t)=>{t.exports={bindAll:Kn(),contains:er(),deepFillIn:nr(),deepMatches:Qn(),deepMixIn:rr(),equals:sr(),every:ir(),fillIn:cr(),filter:lr(),find:ur(),flatten:dr(),forIn:Vn(),forOwn:J(),functions:Hn(),get:pr(),has:mr(),hasOwn:Bn(),keys:hr(),map:gr(),matches:_r(),max:br(),merge:wr(),min:Er(),mixIn:xr(),namespace:Dr(),omit:Ar(),pick:jr(),pluck:Mr(),reduce:Pr(),reject:Fr(),result:Lr(),set:Rr(),size:Nr(),some:$n(),unset:zr(),values:yr()}})),Vr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.default=(0,Br().map)({A:{x:.44758,y:.40745},C:{x:.31006,y:.31616},D50:{x:.34567,y:.35851},D65:{x:.31272,y:.32903},D55:{x:.33243,y:.34744},D75:{x:.29903,y:.31488}},function(e){return[100*(e.x/e.y),100,100*(1-e.x-e.y)/e.y]}),t.exports=e.default})),Hr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=Math,r=n.pow,i=n.sign,a=n.abs,o={decode:function(e){return e<=.04045?e/12.92:r((e+.055)/1.055,2.4)},encode:function(e){return e<=.0031308?12.92*e:1.055*r(e,1/2.4)-.055}},s={encode:function(e){return e<.001953125?16*e:r(e,1/1.8)},decode:function(e){return e<16*.001953125?e/16:r(e,1.8)}};function c(e){return{decode:function(t){return i(t)*r(a(t),e)},encode:function(t){return i(t)*r(a(t),1/e)}}}e.default={sRGB:{r:{x:.64,y:.33},g:{x:.3,y:.6},b:{x:.15,y:.06},gamma:o},"Adobe RGB":{r:{x:.64,y:.33},g:{x:.21,y:.71},b:{x:.15,y:.06},gamma:c(2.2)},"Wide Gamut RGB":{r:{x:.7347,y:.2653},g:{x:.1152,y:.8264},b:{x:.1566,y:.0177},gamma:c(563/256)},"ProPhoto RGB":{r:{x:.7347,y:.2653},g:{x:.1596,y:.8404},b:{x:.0366,y:1e-4},gamma:s}},t.exports=e.default})),Ur=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});function t(e){return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function n(e){return e[0][0]*(e[2][2]*e[1][1]-e[2][1]*e[1][2])+e[1][0]*(e[2][1]*e[0][2]-e[2][2]*e[0][1])+e[2][0]*(e[1][2]*e[0][1]-e[1][1]*e[0][2])}function r(e){var t=1/n(e);return[[(e[2][2]*e[1][1]-e[2][1]*e[1][2])*t,(e[2][1]*e[0][2]-e[2][2]*e[0][1])*t,(e[1][2]*e[0][1]-e[1][1]*e[0][2])*t],[(e[2][0]*e[1][2]-e[2][2]*e[1][0])*t,(e[2][2]*e[0][0]-e[2][0]*e[0][2])*t,(e[1][0]*e[0][2]-e[1][2]*e[0][0])*t],[(e[2][1]*e[1][0]-e[2][0]*e[1][1])*t,(e[2][0]*e[0][1]-e[2][1]*e[0][0])*t,(e[1][1]*e[0][0]-e[1][0]*e[0][1])*t]]}function i(e,t){return[e[0][0]*t[0]+e[0][1]*t[1]+e[0][2]*t[2],e[1][0]*t[0]+e[1][1]*t[1]+e[1][2]*t[2],e[2][0]*t[0]+e[2][1]*t[1]+e[2][2]*t[2]]}function a(e,t){return[[e[0][0]*t[0],e[0][1]*t[1],e[0][2]*t[2]],[e[1][0]*t[0],e[1][1]*t[1],e[1][2]*t[2]],[e[2][0]*t[0],e[2][1]*t[1],e[2][2]*t[2]]]}function o(e,t){return[[e[0][0]*t[0][0]+e[0][1]*t[1][0]+e[0][2]*t[2][0],e[0][0]*t[0][1]+e[0][1]*t[1][1]+e[0][2]*t[2][1],e[0][0]*t[0][2]+e[0][1]*t[1][2]+e[0][2]*t[2][2]],[e[1][0]*t[0][0]+e[1][1]*t[1][0]+e[1][2]*t[2][0],e[1][0]*t[0][1]+e[1][1]*t[1][1]+e[1][2]*t[2][1],e[1][0]*t[0][2]+e[1][1]*t[1][2]+e[1][2]*t[2][2]],[e[2][0]*t[0][0]+e[2][1]*t[1][0]+e[2][2]*t[2][0],e[2][0]*t[0][1]+e[2][1]*t[1][1]+e[2][2]*t[2][1],e[2][0]*t[0][2]+e[2][1]*t[1][2]+e[2][2]*t[2][2]]]}e.transpose=t,e.determinant=n,e.inverse=r,e.multiply=i,e.scalar=a,e.product=o})),Wr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.PI;function n(e){for(var n=e*180/t;n<0;)n+=360;for(;n>360;)n-=360;return n}function r(e){for(var n=t*e/180;n<0;)n+=2*t;for(;n>2*t;)n-=2*t;return n}e.fromRadian=n,e.toRadian=r})),Gr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0});var t=Math.round;function n(e){return e[0]==`#`&&(e=e.slice(1)),e.length<6&&(e=e.split(``).map(function(e){return e+e}).join(``)),e.match(/../g).map(function(e){return parseInt(e,16)/255})}function r(e){return`#`+e.map(function(e){return e=t(255*e).toString(16),e.length<2&&(e=`0`+e),e}).join(``)}e.fromHex=n,e.toHex=r})),Kr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=o(Ur()),r=a(Vr()),i=a(Hr());function a(e){return e&&e.__esModule?e:{default:e}}function o(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function s(){var e=arguments.length<=0||arguments[0]===void 0?i.default.sRGB:arguments[0],t=arguments.length<=1||arguments[1]===void 0?r.default.D65:arguments[1],a=[e.r,e.g,e.b],o=n.transpose(a.map(function(e){return[e.x/e.y,1,(1-e.x-e.y)/e.y]})),s=e.gamma,c=n.multiply(n.inverse(o),t),l=n.scalar(o,c),u=n.inverse(l);return{fromRgb:function(e){return n.multiply(l,e.map(s.decode))},toRgb:function(e){return n.multiply(u,e).map(s.encode)}}}e.default=s,t.exports=e.default})),qr=s(((e,t)=>{t.exports={illuminant:Vr(),workspace:Hr(),matrix:Ur(),degree:Wr(),rgb:Gr(),xyz:Kr()}})),Jr=s((e=>{Object.defineProperty(e,`__esModule`,{value:!0}),e.cfs=e.distance=e.lerp=e.corLerp=void 0;var t=Br();function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);ti/2&&(e>t?t+=i:e+=i),((1-n)*e+n*t)%(i||1/0)}function u(e,t,n){var r={};for(var i in e)r[i]=l(e[i],t[i],n,i);return r}function d(e,t){var n=0;for(var r in e)n+=o(e[r]-t[r],2);return s(n)}function f(e){return t.merge.apply(void 0,r(e.split(``).map(function(e){return n({},e,!0)})))}e.corLerp=l,e.lerp=u,e.distance=d,e.cfs=f})),Yr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=Jr();function a(e,t){var a=arguments.length<=2||arguments[2]===void 0?1e-6:arguments[2],o=-a,s=1+a,c=Math,l=c.min,u=c.max,d=n([`000`,`fff`].map(function(n){return t.fromXyz(e.fromRgb(r.rgb.fromHex(n)))}),2),f=d[0],p=d[1];function m(n){var r=e.toRgb(t.toXyz(n));return[r.map(function(e){return e>=o&&e<=s}).reduce(function(e,t){return e&&t},!0),r]}function h(e,t){for(var r=arguments.length<=2||arguments[2]===void 0?.001:arguments[2];(0,i.distance)(e,t)>r;){var a=(0,i.lerp)(e,t,.5);n(m(a),1)[0]?e=a:t=a}return e}function g(e){return(0,i.lerp)(f,p,e)}function _(e){return e.map(function(e){return u(o,l(s,e))})}return{contains:m,limit:h,spine:g,crop:_}}e.default=a,t.exports=e.default})),Xr=s((e=>{var t=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0}),e.toNotation=e.fromNotation=e.toHue=e.fromHue=void 0;var n=Jr(),r=Math.floor,i=[{s:`R`,h:20.14,e:.8,H:0},{s:`Y`,h:90,e:.7,H:100},{s:`G`,h:164.25,e:1,H:200},{s:`B`,h:237.53,e:1.2,H:300},{s:`R`,h:380.14,e:.8,H:400}],a=i.map(function(e){return e.s}).slice(0,-1).join(``);function o(e){e50){var o=[n,t];t=o[0],n=o[1],i=100-i}return i<1?a[t]:a[t]+i.toFixed()+a[n]}e.fromHue=o,e.toHue=s,e.fromNotation=l,e.toNotation=u})),Zr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,`__esModule`,{value:!0});var r=qr(),i=s(Xr()),a=Jr(),o=Br();function s(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var c=Math,l=c.pow,u=c.sqrt,d=c.exp,f=c.abs,p=c.sign,m=Math,h=m.sin,g=m.cos,_=m.atan2,v={average:{F:1,c:.69,N_c:1},dim:{F:.9,c:.59,N_c:.9},dark:{F:.8,c:.535,N_c:.8}},y=[[.7328,.4296,-.1624],[-.7036,1.6975,.0061],[.003,.0136,.9834]],b=[[.38971,.68898,-.07868],[-.22981,1.1834,.04641],[0,0,1]],x=y,S=r.matrix.inverse(y),C=r.matrix.product(b,r.matrix.inverse(y)),w=r.matrix.product(y,r.matrix.inverse(b)),T={whitePoint:r.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ee=(0,a.cfs)(`QJMCshH`),E=(0,a.cfs)(`JCh`);function D(){var e=arguments.length<=0||arguments[0]===void 0?{}:arguments[0],t=arguments.length<=1||arguments[1]===void 0?ee:arguments[1];e=(0,o.merge)(T,e);var a=e.whitePoint,s=e.adaptingLuminance,c=e.backgroundLuminance,m=v[e.surroundType],b=m.F,D=m.c,O=m.N_c,te=a[1],k=1/(5*s+1),A=.2*l(k,4)*5*s+.1*l(1-l(k,4),2)*l(5*s,1/3),ne=c/te,re=.725*l(1/ne,.2),ie=re,ae=1.48+u(ne),oe=e.discounting?1:b*(1-1/3.6*d(-(s+42)/92)),j=n(r.matrix.multiply(y,a).map(function(e){return oe*te/e+1-oe}),3),se=j[0],M=j[1],ce=j[2],N=pe(de(le(a)));function le(e){var t=n(r.matrix.multiply(x,e),3),i=t[0],a=t[1],o=t[2];return[se*i,M*a,ce*o]}function ue(e){var t=n(e,3),i=t[0],a=t[1],o=t[2];return r.matrix.multiply(S,[i/se,a/M,o/ce])}function de(e){return r.matrix.multiply(C,e).map(function(e){var t=l(A*f(e)/100,.42);return p(e)*400*t/(27.13+t)+.1})}function fe(e){return r.matrix.multiply(w,e.map(function(e){var t=e-.1;return p(t)*100/A*l(27.13*f(t)/(400-f(t)),1/.42)}))}function pe(e){var t=n(e,3),r=t[0],i=t[1],a=t[2];return(r*2+i+a/20-.305)*re}function me(e){return 4/D*u(e/100)*(N+4)*l(A,.25)}function he(e){return 6.25*l(D*e/((N+4)*l(A,.25)),2)}function ge(e){return e*l(A,.25)}function _e(e,t){return l(e/100,2)*t/l(A,.25)}function ve(e){return e/l(A,.25)}function ye(e,t){return 100*u(e/t)}function be(e,t){var n=t.Q,r=t.J,a=t.M,o=t.C,s=t.s,c=t.h,l=t.H,u={};return e.J&&(u.J=isNaN(r)?he(n):r),e.C&&(isNaN(o)?isNaN(a)?(n=isNaN(n)?me(r):n,u.C=_e(s,n)):u.C=ve(a):u.C=t.C),e.h&&(u.h=isNaN(c)?i.toHue(l):c),e.Q&&(u.Q=isNaN(n)?me(r):n),e.M&&(u.M=isNaN(a)?ge(o):a),e.s&&(isNaN(s)?(n=isNaN(n)?me(r):n,a=isNaN(a)?ge(o):a,u.s=ye(a,n)):u.s=s),e.H&&(u.H=isNaN(l)?i.fromHue(c):l),u}function P(e){var i=de(le(e)),a=n(i,3),o=a[0],s=a[1],c=a[2],d=o-s*12/11+c/11,f=(o+s-2*c)/9,p=_(f,d),m=r.degree.fromRadian(p),h=1/4*(g(p+2)+3.8),v=100*l(pe(i)/N,D*ae);return be(t,{J:v,C:l(5e4/13*O*ie*h*u(d*d+f*f)/(o+s+21/20*c),.9)*u(v/100)*l(1.64-l(.29,ne),.73),h:m})}function F(e){var t=be(E,e),n=t.J,i=t.C,a=t.h,o=r.degree.toRadian(a),s=l(i/(u(n/100)*l(1.64-l(.29,ne),.73)),10/9),c=1/4*(g(o+2)+3.8),d=N*l(n/100,1/D/ae),p=5e4/13*O*ie*c/s,m=d/re+.305,_=m*61/20*460/1403,v=61/20*220/1403,y=21/20*6300/1403-27/1403,b=h(o),x=g(o),S,C;return s===0||isNaN(s)?S=C=0:f(b)>=f(x)?(C=_/(p/b+v*x/b+y),S=C*x/b):(S=_/(p/x+v+y*b/x),C=S*b/x),ue(fe([20/61*m+451/1403*S+288/1403*C,20/61*m-891/1403*S-261/1403*C,20/61*m-220/1403*S-6300/1403*C]))}return{fromXyz:P,toXyz:F,fillOut:be}}e.default=D,t.exports=e.default})),Qr=s(((e,t)=>{Object.defineProperty(e,`__esModule`,{value:!0});var n=qr(),r=Math,i=r.sqrt,a=r.pow,o=r.exp,s=r.log,c=r.cos,l=r.sin,u=r.atan2,d={LCD:{K_L:.77,c_1:.007,c_2:.0053},SCD:{K_L:1.24,c_1:.007,c_2:.0363},UCS:{K_L:1,c_1:.007,c_2:.0228}};function f(){var e=d[arguments.length<=0||arguments[0]===void 0?`UCS`:arguments[0]],t=e.K_L,r=e.c_1,f=e.c_2;function p(e){var t=e.J,i=e.M,a=e.h,o=n.degree.toRadian(a),u=(1+100*r)*t/(1+r*t),d=1/f*s(1+f*i);return{J_p:u,a_p:d*c(o),b_p:d*l(o)}}function m(e){var t=e.J_p,s=e.a_p,c=e.b_p,l=-t/(r*t-100*r-1),d=(o(f*i(a(s,2)+a(c,2)))-1)/f,p=u(c,s);return{J:l,M:d,h:n.degree.fromRadian(p)}}function h(e,n){return i(a((e.J_p-n.J_p)/t,2)+a(e.a_p-n.a_p,2)+a(e.b_p-n.b_p,2))}return{fromCam:p,toCam:m,distance:h}}e.default=f,t.exports=e.default})),$r=s(((e,t)=>{var n=Jr(),r=Yr(),i=Zr(),a=Qr(),o=Xr();t.exports={gamut:r,cfs:n.cfs,lerp:n.lerp,cam:i,ucs:a,hq:o}})),ei=l(qr(),1),ti=l($r(),1);function ni(e){let t=new q;return t.rgb_r=e[0],t.rgb_g=e[1],t.rgb_b=e[2],t.rgbToHsluv(),[t.hsluv_h,t.hsluv_s,t.hsluv_l]}function ri(e){let t=new q;return t.hsluv_h=e[0],t.hsluv_s=e[1],t.hsluv_l=e[2],t.hsluvToRgb(),[t.rgb_r,t.rgb_g,t.rgb_b]}var ii=ti.default.cam({whitePoint:ei.default.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ti.default.cfs(`JCh`)),ai=ei.default.xyz(ei.default.workspace.sRGB,ei.default.illuminant.D65),oi=e=>ai.toRgb(ii.toXyz({J:e[0],C:e[1],h:e[2]})),si=e=>{let t=ii.fromXyz(ai.fromRgb(e));return[t.J,t.C,t.h]},[ci,li]=(()=>{let e={k_l:1,c1:.007,c2:.0228},t=Math.PI,n=64/t/5,r=1/(5*n+1),i=.2*r**4*(5*n)+.1*(1-r**4)**2*(5*n)**(1/3);return[n=>{let[r,a,o]=n,s=a*i**.25,c=(1+100*e.c1)*r/(1+e.c1*r);c/=e.k_l;let l=1/e.c2*Math.log(1+e.c2*s),u=l*Math.cos(t/180*o),d=l*Math.sin(t/180*o);return[c,u,d]},n=>{let[r,a,o]=n,s=Math.sqrt(a*a+o*o),c=(Math.exp(s*e.c2)-1)/e.c2,l=(180/t*Math.atan2(o,a)+360)%360,u=c/i**.25;return[r/(1+e.c1*(100-r)),u,l]}]})(),ui=e=>oi(li(e)),di=e=>ci(si(e)),fi=console;fi.color=(e,t=``)=>{let n=K(e).luminance();fi.log(`%c${e} ${t}`,`background-color: ${e};padding: 5px; border-radius: 5px; color: ${n>.5?`#000`:`#fff`}`)},fi.ramp=(e,t=1)=>{fi.log(`%c `,`font-size: 1px;line-height: 16px;background: ${K.getCSSGradient(e,t)};padding: 0 0 0 200px; border-radius: 2px;`)};var pi=(e,t,n,r,i,a,o=.1)=>{if(e===n||t===r)return!0;let s=(r-t)/(n-e),c=(a+i/s-t+s*e)/(s+1/s),l=a+i/s-c/s;return(i-c)**2+(a-l)**2{let i=(t[0]+n[0])/2,a=e(i);return pi(...t,...n,i,a,r)?null:[i,a]},hi=(e,t,n,r=.1)=>{let i=(n-t)/10,a=[];for(let r=t;rMath.round(e*10**t)/10**t,_i=(e,t=1,n=90,r=.005)=>{let i=hi(t=>e(t).gl()[0],0,t,r),a=hi(t=>e(t).gl()[1],0,t,r),o=hi(t=>e(t).gl()[2],0,t,r);return`linear-gradient(${n}deg, ${Array.from(new Set([...i.map(e=>gi(e[0])),...a.map(e=>gi(e[0])),...o.map(e=>gi(e[0]))].sort((e,t)=>e-t))).map(t=>`${e(t).hex()} ${gi(t*100)}%`).join()});`},vi=e=>{e.Color.prototype.jch=function(){return si(this._rgb.slice(0,3).map(e=>e/255))},e.jch=(...t)=>new e.Color(...oi(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.jab=function(){return di(this._rgb.slice(0,3).map(e=>e/255))},e.jab=(...t)=>new e.Color(...ui(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.hsluv=function(){return ni(this._rgb.slice(0,3).map(e=>e/255))},e.hsluv=(...t)=>new e.Color(...ri(t).map(e=>Math.floor(e*255)),`rgb`);let t=e.interpolate,n={jch:si,jab:di,hsluv:ni},r=(e,t,n)=>(Math.abs(e-t)>360/2&&(e>t?t+=360:e+=360),((1-n)*e+n*t)%360);e.interpolate=(i,a,o=.5,s=`lrgb`)=>{if(n[s]){typeof i!=`object`&&(i=new e.Color(i)),typeof a!=`object`&&(a=new e.Color(a));let t=n[s](i.gl()),c=n[s](a.gl()),l=Number.isNaN(i.hsl()[0]),u=Number.isNaN(a.hsl()[0]),d,f,p;switch(s){case`hsluv`:t[1]<1e-10&&(t[0]=c[0]),t[1]===0&&(t[1]=c[1]),c[1]<1e-10&&(c[0]=t[0]),c[1]===0&&(c[1]=t[1]),d=r(t[0],c[0],o),f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o;break;case`jch`:l&&(t[2]=c[2]),u&&(c[2]=t[2]),d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=r(t[2],c[2],o);break;default:d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o}return e[s](d,f,p).alpha(i.alpha()+o*(a.alpha()-i.alpha()))}return t(i,a,o,s)},e.getCSSGradient=_i},X={mainTRC:2.4,get mainTRCencode(){return 1/this.mainTRC},sRco:.2126729,sGco:.7151522,sBco:.072175,normBG:.56,normTXT:.57,revTXT:.62,revBG:.65,blkThrs:.022,blkClmp:1.414,scaleBoW:1.14,scaleWoB:1.14,loBoWoffset:.027,loWoBoffset:.027,deltaYmin:5e-4,loClip:.1,mFactor:1.9468554433171,get mFactInv(){return 1/this.mFactor},mOffsetIn:.0387393816571401,mExpAdj:.283343396420869,get mExp(){return this.mExpAdj/this.blkClmp},mOffsetOut:.312865795870758};function yi(e,t,n=-1){let r=[0,1.1];if(isNaN(e)||isNaN(t)||Math.min(e,t)r[1])return 0;let i=0,a=0,o=`BoW`;return e=e>X.blkThrs?e:e+(X.blkThrs-e)**+X.blkClmp,t=t>X.blkThrs?t:t+(X.blkThrs-t)**+X.blkClmp,Math.abs(t-e)e?(i=(t**+X.normBG-e**+X.normTXT)*X.scaleBoW,a=i-X.loClip?0:i+X.loWoBoffset),n<0?a*100:n==0?Math.round(Math.abs(a)*100)+``+o+``:Number.isInteger(n)?(a*100).toFixed(n):0)}function bi(e=[0,0,0]){function t(e){return(e/255)**X.mainTRC}return X.sRco*t(e[0])+X.sGco*t(e[1])+X.sBco*t(e[2])}var xi=(e,t,n,r,i,a,o,s,c)=>{let l=1-c,u=l*l,d=u*l,f=c*c*c;return{x:d*e+u*3*c*n+l*3*c*c*i+f*o,y:d*t+u*3*c*r+l*3*c*c*a+f*s}},Si=(e,t)=>{let n=[],r={x:+e[0],y:+e[1]};for(let i=0,a=e.length;a-2*!t>i;i+=2){let o=[{x:+e[i-2],y:+e[i-1]},{x:+e[i],y:+e[i+1]},{x:+e[i+2],y:+e[i+3]},{x:+e[i+4],y:+e[i+5]}];t?i?a-4===i?o[3]={x:+e[0],y:+e[1]}:a-2===i&&(o[2]={x:+e[0],y:+e[1]},o[3]={x:+e[2],y:+e[3]}):o[0]={x:+e[a-2],y:+e[a-1]}:a-4===i?o[3]=o[2]:i||(o[0]={x:+e[i],y:+e[i+1]}),n.push([r.x,r.y,(-o[0].x+6*o[1].x+o[2].x)/6,(-o[0].y+6*o[1].y+o[2].y)/6,(o[1].x+6*o[2].x-o[3].x)/6,(o[1].y+6*o[2].y-o[3].y)/6,o[2].x,o[2].y]),r=o[2]}return n},Ci=(e,t,n,r,i,a,o,s)=>{let c=e,l=t,u=0;for(let d=1;d<5;d++){let{x:f,y:p}=xi(e,t,n,r,i,a,o,s,d/5);u+=Math.hypot(f-c,p-l),c=f,l=p}return u+=Math.hypot(o-c,s-l),u},wi=(e,t,n,r,i,a,o,s)=>{let c=Math.floor(Ci(e,t,n,r,i,a,o,s)*.75),l=[],u=0;for(let d=0;d<=c;d++){let f=xi(e,t,n,r,i,a,o,s,d/c),p=Math.round(f.x);if(l[p]=f.y,p-u>1){let e=l[u],t=l[p];for(let n=u+1;nl[Math.round(e)]||null},Ti={CAM02:`jab`,CAM02p:`jch`,HEX:`hex`,HSL:`hsl`,HSLuv:`hsluv`,HSV:`hsv`,LAB:`lab`,LCH:`lch`,RGB:`rgb`,OKLAB:`oklab`,OKLCH:`oklch`};function Z(e,t=0){let n=10**t;return Math.round(e*n)/n}function Ei(e,t){let n;return n=e>1?(e-1)*t+1:e<-1?(e+1)*t-1:1,Z(n,2)}function Di(e){return K(String(e)).jch()}function Oi(e){return K(String(e)).hsluv()}function ki(e,t,n){let r=[[],[],[]];if(e.forEach((e,n)=>r.forEach((r,i)=>r.push(t[n],e[i]))),n===`hcl`){let e=r[1];for(let t=1;t{let t=[];for(let n=1;n{e[t]=e[n]}),t.length=0;break}if(t.length){let n=K(`#ccc`).jch()[2];t.forEach(t=>{e[t]=n})}t.length=0;for(let n=e.length-1;n>0;n-=2)if(Number.isNaN(e[n]))t.push(n);else{t.forEach(t=>{e[t]=e[n]});break}for(let t=1;tSi(e).map(e=>wi(...e)));return e=>{let t=i.map(t=>{for(let n=0;nr*t**e+i}function ji({swatches:e,colorKeys:t,colorspace:n,colorSpace:r=n??`LAB`,shift:i=1,fullScale:a=!0,smooth:o=!1,distributeLightness:s=`linear`,sortColor:c=!0,asFun:l=!1}={}){n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead.");let u=Ti[r];if(!u)throw Error(`Colorspace “${r}” not supported`);if(!t)throw Error(`Colorkeys missing: returned “${t}”`);let d;if(a)d=t.map(t=>e-e*(K(t).jch()[0]/100)).sort((e,t)=>e-t).concat(e),d.unshift(0);else{let n=t.map(e=>K(e).jch()[0]/100),r=Math.min(...n),i=Math.max(...n);d=n.map(t=>t===0||isNaN((t-r)/(i-r))?0:e-(t-r)/(i-r)*e).sort((e,t)=>e-t)}let f=Ai(i,[1,e],[1,e]);if(f=d.map(e=>Math.max(0,f(e))),d=f,s===`polynomial`){let t=e=>Math.sqrt(Math.sqrt((e**2.25+e**4)/2));d=f.map(t=>t/e).map(n=>t(n)*e)}let p=t.map((e,t)=>({colorKeys:Di(e),index:t})).sort((e,t)=>t.colorKeys[0]-e.colorKeys[0]).map(e=>t[e.index]),m=[],h;if(a){let e=u===`lch`?K.lch(...K(`#fff`).lch()):`#ffffff`,t=u===`lch`?K.lch(...K(`#000`).lch()):`#000000`;m=[e,...p,t]}else m=c?p:t;let g;if(o){let t=m;if(m=m.map(e=>K(String(e))[u]()),u===`hcl`&&m.forEach(e=>{e[1]=Number.isNaN(e[1])?0:e[1]}),u===`jch`)for(let e=0;eh(t))}else h=K.scale(m.map(e=>typeof e==`object`&&e.constructor===K.Color?e:String(e))).domain(d).mode(u);return l?h:(!o||o===!1?h.colors(e):g).filter(e=>e!=null)}function Mi(e,t){let n=[],r={};return Object.keys(e).forEach(n=>{r[e[n][t]]=e[n]}),Object.keys(r).forEach(e=>n.push(r[e])),n}function Ni(e){return Number.isNaN(e)?0:e}function Pi(e,t,n=!1){if(!e)throw Error(`Cannot convert color value of “${e}”`);if(!Ti[t])throw Error(`Cannot convert to colorspace “${t}”`);let r=Ti[t],i=K(String(e))[r]();if(t===`HSL`&&i.pop(),t===`HEX`){if(n){let t=K(String(e)).rgb();return{r:t[0],g:t[1],b:t[2]}}return i}let a={},o=i.map(Ni);o=o.map((e,t)=>{let i=Z(e),o=t;r===`hsluv`&&(o+=2);let s=r.charAt(o);return r===`jch`&&s===`c`&&(s=`C`),a[s===`j`?`J`:s]=i,r in{lab:1,lch:1,jab:1,jch:1}?n||(s===`l`||s===`j`)&&(i+=`%`):r!==`hsluv`&&(s===`s`||s===`l`||s===`v`)&&(a[s]=Z(e,2),n||(i=Z(e*100),i+=`%`)),i});let s=`${r}(${o.join(`, `)})`;return n?a:s}function Fi(e,t,n){let r=[e,t,n].map(e=>(e/=255,e<=.03928?e/12.92:((e+.055)/1.055)**2.4));return r[0]*.2126+r[1]*.7152+r[2]*.0722}function Ii(e,t,n,r=`wcag2`){if(n===void 0){let e=K.rgb(...t).hsluv()[2];n=Z(e/100,2)}if(r===`wcag2`){let r=Fi(e[0],e[1],e[2]),i=Fi(t[0],t[1],t[2]),a=(r+.05)/(i+.05),o=(i+.05)/(r+.05);return n<.5?a>=1?a:-o:a<1?o:a===1?a:-a}else if(r===`wcag3`)return n<.5?yi(bi(e),bi(t))*-1:yi(bi(e),bi(t));else throw Error(`Contrast calculation method ${r} unsupported; use 'wcag2' or 'wcag3'`)}function Li(e,t){if(!e)throw Error(`Array undefined`);if(!Array.isArray(e))throw Error(`Passed object is not an array`);let n=t===`wcag2`?0:1;return Math.min(...e.filter(e=>e>=n))}function Ri(e,t){if(!e)throw Error(`Ratios undefined`);e=e.sort((e,t)=>e-t);let n=Li(e,t),r=e.indexOf(n),i=[],a=e.slice(0,r),o=e.slice(r,e.length);for(let e=0;ee-t),i}var zi=(e,t,n,r,i)=>{let a=3e3,o=ji({swatches:a,colorKeys:e._modifiedKeys,colorspace:e._colorspace,shift:1,smooth:e._smooth,asFun:!0}),s={},c=e=>{if(s[e])return s[e];let r=Ii(K(o(e)).rgb(),t,n,i);return s[e]=r,r},l=e=>{let t=c(0).01&&o;)o--,n/=2,iu.push(o(l(+e)))),u},Q=class{constructor({name:e,colorKeys:t,colorspace:n,colorSpace:r=n??`RGB`,ratios:i,smooth:a=!1,output:o=`HEX`,saturation:s=100}){if(n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),this._name=e,this._colorKeys=t,this._modifiedKeys=t,this._colorspace=r,this._ratios=i,this._smooth=a,this._output=o,this._saturation=s,!this._name)throw Error(`Color missing name`);if(!this._colorKeys)throw Error(`Color Keys are undefined`);if(!Ti[this._colorspace])throw Error(`Colorspace “${r}” not supported`);if(!Ti[this._output])throw Error(`Output “${this._output}” not supported`);for(let e=0;e{let n=K(`${t}`).oklch(),r=n[1]*(this._saturation/100),i=K.oklch(n[0],r,n[2]),a=K.rgb(i).hex();e.push(a)}),this._modifiedKeys=e,this._generateColorScale()}_generateColorScale(){this._colorScale=ji({swatches:3e3,colorKeys:this._modifiedKeys,colorSpace:this._colorspace,shift:1,smooth:this._smooth,asFun:!0})}},Bi=class extends Q{get backgroundColorScale(){return this._backgroundColorScale||this._generateColorScale(),this._backgroundColorScale}_generateColorScale(){Q.prototype._generateColorScale.call(this);let e=ji({swatches:1e3,colorKeys:this._colorKeys,colorspace:this._colorspace,shift:1,smooth:this._smooth});e.push(...this.colorKeys);let t=Mi(e.map((e,t)=>({value:Math.round(Oi(e)[2]),index:t})),`value`).map(t=>e[t.index]);return t.length>=101&&(t.length=100,t.push(`#ffffff`)),this._backgroundColorScale=t.map(e=>Pi(e,this._output)),this._backgroundColorScale}},Vi=class{constructor({colors:e,backgroundColor:t,lightness:n,contrast:r=1,saturation:i=100,output:a=`HEX`,formula:o=`wcag2`}){if(this._output=a,this._colors=e,this._lightness=n,this._saturation=i,this._formula=o,this._setBackgroundColor(t),this._setBackgroundColorValue(),this._contrast=r,!this._colors)throw Error(`No colors are defined`);if(!this._backgroundColor)throw Error(`Background color is undefined`);if(e.forEach(e=>{if(!e.ratios)throw Error(`Color ${e.name}'s ratios are undefined`)}),!Ti[this._output])throw Error(`Output “${a}” not supported`);this._saturation<100&&this._updateColorSaturation(this._saturation),this._findContrastColors(),this._findContrastColorPairs(),this._findContrastColorValues()}set formula(e){this._formula=e,this._findContrastColors()}get formula(){return this._formula}set contrast(e){this._contrast=e,this._findContrastColors()}get contrast(){return this._contrast}set lightness(e){this._lightness=e,this._setBackgroundColor(this._backgroundColor),this._findContrastColors()}get lightness(){return this._lightness}set saturation(e){this._saturation=e,this._updateColorSaturation(e),this._findContrastColors()}get saturation(){return this._saturation}set backgroundColor(e){this._setBackgroundColor(e),this._findContrastColors()}get backgroundColorValue(){return this._backgroundColorValue}get backgroundColor(){return this._backgroundColor}set colors(e){this._colors=e,this._findContrastColors()}get colors(){return this._colors}set addColor(e){this._colors.push(e),this._findContrastColors()}set removeColor(e){let t=this._colors.filter(t=>t.name!==e.name);this._colors=t,this._findContrastColors()}set updateColor(e){if(Array.isArray(e))for(let t=0;tn.name===e[t].color);n=n[0];let r=this._colors.indexOf(n),i=this._colors.filter(n=>n.name!==e[t].color);e[t].name&&(n.name=e[t].name),e[t].colorKeys&&(n.colorKeys=e[t].colorKeys),e[t].ratios&&(n.ratios=e[t].ratios),(e[t].colorSpace!==void 0||e[t].colorspace!==void 0)&&(e[t].colorspace!==void 0&&e[t].colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),n.colorSpace=e[t].colorSpace??e[t].colorspace),e[t].smooth&&(n.smooth=e[t].smooth),n._generateColorScale(),i.splice(r,0,n),this._colors=i}else{let t=this._colors.filter(t=>t.name===e.color);t=t[0];let n=this._colors.indexOf(t),r=this._colors.filter(t=>t.name!==e.color);e.name&&(t.name=e.name),e.colorKeys&&(t.colorKeys=e.colorKeys),e.ratios&&(t.ratios=e.ratios),(e.colorSpace!==void 0||e.colorspace!==void 0)&&(e.colorspace!==void 0&&e.colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),t.colorSpace=e.colorSpace??e.colorspace),e.smooth&&(t.smooth=e.smooth),t._generateColorScale(),r.splice(n,0,t),this._colors=r}this._findContrastColors()}set output(e){this._output=e,this._colors.forEach(e=>{e.output=this._output}),this._backgroundColor.output=this._output,this._findContrastColors()}get output(){return this._output}get contrastColors(){return this._contrastColors}get contrastColorPairs(){return this._contrastColorPairs}get contrastColorValues(){return this._contrastColorValues}_setBackgroundColor(e){if(typeof e==`string`){let t=new Bi({name:`background`,colorKeys:[e],output:`RGB`}),n=Z(K(String(e)).hsluv()[2]);this._backgroundColor=t,this._lightness=n,this._backgroundColorValue=t[this._lightness]}else{e.output=`RGB`;let t=e.backgroundColorScale[this._lightness];this._backgroundColor=e,this._backgroundColorValue=t}}_setBackgroundColorValue(){this._backgroundColorValue=this._backgroundColor.backgroundColorScale[this._lightness]}_updateColorSaturation(e){this._colors.map(t=>{t.saturation=e})}_findContrastColors(){let e=K(String(this._backgroundColorValue)).rgb(),t=this._lightness/100,n={background:Pi(this._backgroundColorValue,this._output)},r=[],i=[],a={...n};return r.push(n),this._colors.map(n=>{if(n.ratios!==void 0){let o,s=[],c={name:n.name,values:s},l;Array.isArray(n.ratios)?l=n.ratios:Array.isArray(n.ratios)||(o=Object.keys(n.ratios),l=Object.values(n.ratios)),l=l.map(e=>Ei(+e,this._contrast));let u=zi(n,e,t,l,this._formula).map(e=>Pi(e,this._output));for(let e=0;e{let t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return[Number.parseInt(t[1],16),Number.parseInt(t[2],16),Number.parseInt(t[3],16)]},Wi=(e,t,n)=>{let r=e/255,i=t/255,a=n/255,o=Math.min(r,i,a),s=Math.max(r,i,a),c=s-o,l=0,u=0,d=0;return l=c===0?0:s===r?(i-a)/c%6:s===i?(a-r)/c+2:(r-i)/c+4,l=Math.round(l*60),l<0&&(l+=360),d=(s+o)/2,u=c===0?0:c/(1-Math.abs(2*d-1)),u=+(u*100).toFixed(1),d=+(d*100).toFixed(1),[l,u,Math.round(d)]},Gi=(e,t,n,r)=>{let i=n/100,a=t*Math.min(i,1-i)/100,o=t=>{let n=(t+e/30)%12,r=i-a*Math.max(Math.min(n-3,9-n,1),-1);return Math.round(255*r).toString(16).padStart(2,`0`).toUpperCase()},s=o(0),c=o(8),l=o(4),u=((e,t,n)=>Math.min(Math.max(e,t),n))(r,0,1);return`#${s}${c}${l}${Math.round(u*255).toString(16).padStart(2,`0`).toUpperCase()}`},Ki=(e,t,n=1)=>{let r=Ui(e),i=Ui(t===`white`?`#FFFFFF`:t===`black`?`#000000`:t),a=r.map((e,t)=>[(e-i[t])/(255-i[t]),(e-i[t])/(0-i[t])]),o=Hi(Math.max(...a.flat().filter(e=>/^-?\d+\.?\d*$/.test(e)))),s=r.map((e,t)=>Math.round((e-i[t]+i[t]*o)/o));if(s.includes(NaN)){let e=Wi(r[0],r[1],r[2]);return{h:e[0],s:Math.round(e[1]*n),l:e[2],a:1}}let c=Wi(s[0],s[1],s[2]);return{h:c[0],s:Math.round(c[1]*n),l:c[2],a:o}},qi={backgroundColor:`gray`,colorSpace:`OKLCH`,colorSmoothing:!1,formula:`wcag2`,output:`HEX`,colors:{gray:[$(215,20,90),$(215,8,50),$(215,6,25)],red:[$(358,100,58),$(350,100,30)],orange:[$(32,100,48),$(12,100,30)],yellow:[$(50,100,50),$(25,100,20)],lime:[$(100,68,50),$(115,86,25)],green:[$(163,87,42),$(168,100,25)],cyan:[$(185,80,45),$(200,98,35)],blue:[$(212,98,46),$(222,95,25)],purple:[$(258,94,64),$(265,100,35)],fuchsia:[$(295,56,50),$(285,80,25)],pink:[$(334,90,50),$(330,91,25)]},themes:{light:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16.75],contrast:1,lightness:100,saturation:100},dark:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16],contrast:1,lightness:6,saturation:97},lightHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:100,saturation:100},darkHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:6,saturation:97}}};function $(e,t,n){return K.hsl(e,t/100,n/100).hex()}function Ji(e,t){let n=e.colorSpace,r=e.colorSmoothing,i=e.themes[t].ratios,a=new Bi({name:`gray`,colorKeys:e.colors.gray,colorspace:n,ratios:i,smooth:r}),o=new Q({name:`blue`,colorKeys:e.colors.blue,colorspace:n,ratios:i,smooth:r}),s=new Q({name:`cyan`,colorKeys:e.colors.cyan,colorspace:n,ratios:i,smooth:r}),c=new Q({name:`fuchsia`,colorKeys:e.colors.fuchsia,colorspace:n,ratios:i,smooth:r}),l=new Q({name:`green`,colorKeys:e.colors.green,colorspace:n,ratios:i,smooth:r}),u=new Q({name:`lime`,colorKeys:e.colors.lime,colorspace:n,ratios:i,smooth:r}),d=new Q({name:`orange`,colorKeys:e.colors.orange,colorspace:n,ratios:i,smooth:r}),f=new Q({name:`pink`,colorKeys:e.colors.pink,colorspace:n,ratios:i,smooth:r}),p=new Q({name:`purple`,colorKeys:e.colors.purple,colorspace:n,ratios:i,smooth:r}),m={gray:a,red:new Q({name:`red`,colorKeys:e.colors.red,colorspace:n,ratios:i,smooth:r}),orange:d,yellow:new Q({name:`yellow`,colorKeys:e.colors.yellow,colorspace:n,ratios:i,smooth:r}),lime:u,green:l,cyan:s,blue:o,purple:p,fuchsia:c,pink:f};return e.colors.custom&&(m.custom=new Q({name:`custom`,colorKeys:e.colors.custom,colorspace:n,ratios:i,smooth:r})),new Vi({colors:Object.values(m),backgroundColor:m[e.backgroundColor],contrast:e.themes[t].contrast,lightness:e.themes[t].lightness,saturation:e.themes[t].saturation,output:e.output,formula:e.formula}).contrastColors}function Yi(e){let t={};for(let n of Object.keys(e.themes))t[n]=Ji(e,n);return t}function Xi(e){qi.colors.custom=[e];let t=Yi(qi);return Object.fromEntries(Object.entries(t).map(([e,t])=>{let n=t.find(e=>e&&e.name===`custom`),r=Object.fromEntries(n.values.map(({name:e,value:t})=>[e,t]));for(let[e,n]of Object.entries(r)){let i=Ki(n,t[0].background);r[`alpha${e.charAt(0).toUpperCase()+e.slice(1)}`]=Gi(i.h,i.s,i.l,i.a)}return[e,r]}))}return e.generateCustomColors=Xi,e.generateThemesJson=Yi,e.hslToHex=$,e.leonardoConfig=qi,e})({}); \ No newline at end of file +var CompoundTheme=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;lt[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),{min:u,max:d}=Math,f=(e,t=0,n=1)=>u(d(t,e),n),p=e=>{e._clipped=!1,e._unclipped=e.slice(0);for(let t=0;t<=3;t++)t<3?((e[t]<0||e[t]>255)&&(e._clipped=!0),e[t]=f(e[t],0,255)):t===3&&(e[t]=f(e[t],0,1));return e},m={};for(let e of[`Boolean`,`Number`,`String`,`Function`,`Array`,`Date`,`RegExp`,`Undefined`,`Null`])m[`[object ${e}]`]=e.toLowerCase();function h(e){return m[Object.prototype.toString.call(e)]||`object`}var g=(e,t=null)=>e.length>=3?Array.prototype.slice.call(e):h(e[0])==`object`&&t?t.split(``).filter(t=>e[0][t]!==void 0).map(t=>e[0][t]):e[0].slice(0),_=e=>{if(e.length<2)return null;let t=e.length-1;return h(e[t])==`string`?e[t].toLowerCase():null},{PI:v,min:y,max:b}=Math,x=e=>Math.round(e*100)/100,S=e=>Math.round(e*100)/100,C=v*2,w=v/3,T=v/180,ee=180/v;function E(e){return[...e.slice(0,3).reverse(),...e.slice(3)]}var D={format:{},autodetect:[]},O=class{constructor(...e){let t=this;if(h(e[0])===`object`&&e[0].constructor&&e[0].constructor===this.constructor)return e[0];let n=_(e),r=!1;if(!n){r=!0,D.sorted||=(D.autodetect=D.autodetect.sort((e,t)=>t.p-e.p),!0);for(let t of D.autodetect)if(n=t.test(...e),n)break}if(D.format[n])t._rgb=p(D.format[n].apply(null,r?e:e.slice(0,-1)));else throw Error(`unknown format: `+e);t._rgb.length===3&&t._rgb.push(1)}toString(){return h(this.hex)==`function`?this.hex():`[${this._rgb.join(`,`)}]`}},te=`3.2.0`,k=(...e)=>new O(...e);k.version=te;var A={aliceblue:`#f0f8ff`,antiquewhite:`#faebd7`,aqua:`#00ffff`,aquamarine:`#7fffd4`,azure:`#f0ffff`,beige:`#f5f5dc`,bisque:`#ffe4c4`,black:`#000000`,blanchedalmond:`#ffebcd`,blue:`#0000ff`,blueviolet:`#8a2be2`,brown:`#a52a2a`,burlywood:`#deb887`,cadetblue:`#5f9ea0`,chartreuse:`#7fff00`,chocolate:`#d2691e`,coral:`#ff7f50`,cornflowerblue:`#6495ed`,cornsilk:`#fff8dc`,crimson:`#dc143c`,cyan:`#00ffff`,darkblue:`#00008b`,darkcyan:`#008b8b`,darkgoldenrod:`#b8860b`,darkgray:`#a9a9a9`,darkgreen:`#006400`,darkgrey:`#a9a9a9`,darkkhaki:`#bdb76b`,darkmagenta:`#8b008b`,darkolivegreen:`#556b2f`,darkorange:`#ff8c00`,darkorchid:`#9932cc`,darkred:`#8b0000`,darksalmon:`#e9967a`,darkseagreen:`#8fbc8f`,darkslateblue:`#483d8b`,darkslategray:`#2f4f4f`,darkslategrey:`#2f4f4f`,darkturquoise:`#00ced1`,darkviolet:`#9400d3`,deeppink:`#ff1493`,deepskyblue:`#00bfff`,dimgray:`#696969`,dimgrey:`#696969`,dodgerblue:`#1e90ff`,firebrick:`#b22222`,floralwhite:`#fffaf0`,forestgreen:`#228b22`,fuchsia:`#ff00ff`,gainsboro:`#dcdcdc`,ghostwhite:`#f8f8ff`,gold:`#ffd700`,goldenrod:`#daa520`,gray:`#808080`,green:`#008000`,greenyellow:`#adff2f`,grey:`#808080`,honeydew:`#f0fff0`,hotpink:`#ff69b4`,indianred:`#cd5c5c`,indigo:`#4b0082`,ivory:`#fffff0`,khaki:`#f0e68c`,laserlemon:`#ffff54`,lavender:`#e6e6fa`,lavenderblush:`#fff0f5`,lawngreen:`#7cfc00`,lemonchiffon:`#fffacd`,lightblue:`#add8e6`,lightcoral:`#f08080`,lightcyan:`#e0ffff`,lightgoldenrod:`#fafad2`,lightgoldenrodyellow:`#fafad2`,lightgray:`#d3d3d3`,lightgreen:`#90ee90`,lightgrey:`#d3d3d3`,lightpink:`#ffb6c1`,lightsalmon:`#ffa07a`,lightseagreen:`#20b2aa`,lightskyblue:`#87cefa`,lightslategray:`#778899`,lightslategrey:`#778899`,lightsteelblue:`#b0c4de`,lightyellow:`#ffffe0`,lime:`#00ff00`,limegreen:`#32cd32`,linen:`#faf0e6`,magenta:`#ff00ff`,maroon:`#800000`,maroon2:`#7f0000`,maroon3:`#b03060`,mediumaquamarine:`#66cdaa`,mediumblue:`#0000cd`,mediumorchid:`#ba55d3`,mediumpurple:`#9370db`,mediumseagreen:`#3cb371`,mediumslateblue:`#7b68ee`,mediumspringgreen:`#00fa9a`,mediumturquoise:`#48d1cc`,mediumvioletred:`#c71585`,midnightblue:`#191970`,mintcream:`#f5fffa`,mistyrose:`#ffe4e1`,moccasin:`#ffe4b5`,navajowhite:`#ffdead`,navy:`#000080`,oldlace:`#fdf5e6`,olive:`#808000`,olivedrab:`#6b8e23`,orange:`#ffa500`,orangered:`#ff4500`,orchid:`#da70d6`,palegoldenrod:`#eee8aa`,palegreen:`#98fb98`,paleturquoise:`#afeeee`,palevioletred:`#db7093`,papayawhip:`#ffefd5`,peachpuff:`#ffdab9`,peru:`#cd853f`,pink:`#ffc0cb`,plum:`#dda0dd`,powderblue:`#b0e0e6`,purple:`#800080`,purple2:`#7f007f`,purple3:`#a020f0`,rebeccapurple:`#663399`,red:`#ff0000`,rosybrown:`#bc8f8f`,royalblue:`#4169e1`,saddlebrown:`#8b4513`,salmon:`#fa8072`,sandybrown:`#f4a460`,seagreen:`#2e8b57`,seashell:`#fff5ee`,sienna:`#a0522d`,silver:`#c0c0c0`,skyblue:`#87ceeb`,slateblue:`#6a5acd`,slategray:`#708090`,slategrey:`#708090`,snow:`#fffafa`,springgreen:`#00ff7f`,steelblue:`#4682b4`,tan:`#d2b48c`,teal:`#008080`,thistle:`#d8bfd8`,tomato:`#ff6347`,turquoise:`#40e0d0`,violet:`#ee82ee`,wheat:`#f5deb3`,white:`#ffffff`,whitesmoke:`#f5f5f5`,yellow:`#ffff00`,yellowgreen:`#9acd32`},ne=/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,re=/^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/,ie=e=>{if(e.match(ne)){(e.length===4||e.length===7)&&(e=e.substr(1)),e.length===3&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);let t=parseInt(e,16);return[t>>16,t>>8&255,t&255,1]}if(e.match(re)){(e.length===5||e.length===9)&&(e=e.substr(1)),e.length===4&&(e=e.split(``),e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]+e[3]+e[3]);let t=parseInt(e,16);return[t>>24&255,t>>16&255,t>>8&255,Math.round((t&255)/255*100)/100]}throw Error(`unknown hex color: ${e}`)},{round:ae}=Math,oe=(...e)=>{let[t,n,r,i]=g(e,`rgba`),a=_(e)||`auto`;i===void 0&&(i=1),a===`auto`&&(a=i<1?`rgba`:`rgb`),t=ae(t),n=ae(n),r=ae(r);let o=`000000`+(t<<16|n<<8|r).toString(16);o=o.substr(o.length-6);let s=`0`+ae(i*255).toString(16);switch(s=s.substr(s.length-2),a.toLowerCase()){case`rgba`:return`#${o}${s}`;case`argb`:return`#${s}${o}`;default:return`#${o}`}};O.prototype.name=function(){let e=oe(this._rgb,`rgb`);for(let t of Object.keys(A))if(A[t]===e)return t.toLowerCase();return e},D.format.named=e=>{if(e=e.toLowerCase(),A[e])return ie(A[e]);throw Error(`unknown color name: `+e)},D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&A[e.toLowerCase()])return`named`}}),O.prototype.alpha=function(e,t=!1){return e!==void 0&&h(e)===`number`?t?(this._rgb[3]=e,this):new O([this._rgb[0],this._rgb[1],this._rgb[2],e],`rgb`):this._rgb[3]},O.prototype.clipped=function(){return this._rgb._clipped||!1};var j={Kn:18,labWhitePoint:`d65`,Xn:.95047,Yn:1,Zn:1.08883,t0:.137931034,t1:.206896552,t2:.12841855,t3:.008856452,kE:216/24389,kKE:8,kK:24389/27,RefWhiteRGB:{X:.95047,Y:1,Z:1.08883},MtxRGB2XYZ:{m00:.4124564390896922,m01:.21267285140562253,m02:.0193338955823293,m10:.357576077643909,m11:.715152155287818,m12:.11919202588130297,m20:.18043748326639894,m21:.07217499330655958,m22:.9503040785363679},MtxXYZ2RGB:{m00:3.2404541621141045,m01:-.9692660305051868,m02:.055643430959114726,m10:-1.5371385127977166,m11:1.8760108454466942,m12:-.2040259135167538,m20:-.498531409556016,m21:.041556017530349834,m22:1.0572251882231791},As:.9414285350000001,Bs:1.040417467,Cs:1.089532651,MtxAdaptMa:{m00:.8951,m01:-.7502,m02:.0389,m10:.2664,m11:1.7135,m12:-.0685,m20:-.1614,m21:.0367,m22:1.0296},MtxAdaptMaI:{m00:.9869929054667123,m01:.43230526972339456,m02:-.008528664575177328,m10:-.14705425642099013,m11:.5183602715367776,m12:.04004282165408487,m20:.15996265166373125,m21:.0492912282128556,m22:.9684866957875502}},se=new Map([[`a`,[1.0985,.35585]],[`b`,[1.0985,.35585]],[`c`,[.98074,1.18232]],[`d50`,[.96422,.82521]],[`d55`,[.95682,.92149]],[`d65`,[.95047,1.08883]],[`e`,[1,1,1]],[`f2`,[.99186,.67393]],[`f7`,[.95041,1.08747]],[`f11`,[1.00962,.6435]],[`icc`,[.96422,.82521]]]);function M(e){let t=se.get(String(e).toLowerCase());if(!t)throw Error(`unknown Lab illuminant `+e);j.labWhitePoint=e,j.Xn=t[0],j.Zn=t[1]}function ce(){return j.labWhitePoint}var N=(...e)=>{e=g(e,`lab`);let[t,n,r]=e,[i,a,o]=le(t,n,r),[s,c,l]=de(i,a,o);return[s,c,l,e.length>3?e[3]:1]},le=(e,t,n)=>{let{kE:r,kK:i,kKE:a,Xn:o,Yn:s,Zn:c}=j,l=(e+16)/116,u=.002*t+l,d=l-.005*n,f=u*u*u,p=d*d*d,m=f>r?f:(116*u-16)/i,h=e>a?((e+16)/116)**3:e/i,g=p>r?p:(116*d-16)/i;return[m*o,h*s,g*c]},ue=e=>{let t=Math.sign(e);return e=Math.abs(e),(e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055)*t},de=(e,t,n)=>{let{MtxAdaptMa:r,MtxAdaptMaI:i,MtxXYZ2RGB:a,RefWhiteRGB:o,Xn:s,Yn:c,Zn:l}=j,u=s*r.m00+c*r.m10+l*r.m20,d=s*r.m01+c*r.m11+l*r.m21,f=s*r.m02+c*r.m12+l*r.m22,p=o.X*r.m00+o.Y*r.m10+o.Z*r.m20,m=o.X*r.m01+o.Y*r.m11+o.Z*r.m21,h=o.X*r.m02+o.Y*r.m12+o.Z*r.m22,g=(e*r.m00+t*r.m10+n*r.m20)*(p/u),_=(e*r.m01+t*r.m11+n*r.m21)*(m/d),v=(e*r.m02+t*r.m12+n*r.m22)*(h/f),y=g*i.m00+_*i.m10+v*i.m20,b=g*i.m01+_*i.m11+v*i.m21,x=g*i.m02+_*i.m12+v*i.m22,S=ue(y*a.m00+b*a.m10+x*a.m20),C=ue(y*a.m01+b*a.m11+x*a.m21),w=ue(y*a.m02+b*a.m12+x*a.m22);return[S*255,C*255,w*255]},fe=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=he(t,n,r),[c,l,u]=pe(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function pe(e,t,n){let{Xn:r,Yn:i,Zn:a,kE:o,kK:s}=j,c=e/r,l=t/i,u=n/a,d=c>o?c**(1/3):(s*c+16)/116,f=l>o?l**(1/3):(s*l+16)/116,p=u>o?u**(1/3):(s*u+16)/116;return[116*f-16,500*(d-f),200*(f-p)]}function me(e){let t=Math.sign(e);return e=Math.abs(e),(e<=.04045?e/12.92:((e+.055)/1.055)**2.4)*t}var he=(e,t,n)=>{e=me(e/255),t=me(t/255),n=me(n/255);let{MtxRGB2XYZ:r,MtxAdaptMa:i,MtxAdaptMaI:a,Xn:o,Yn:s,Zn:c,As:l,Bs:u,Cs:d}=j,f=e*r.m00+t*r.m10+n*r.m20,p=e*r.m01+t*r.m11+n*r.m21,m=e*r.m02+t*r.m12+n*r.m22,h=o*i.m00+s*i.m10+c*i.m20,g=o*i.m01+s*i.m11+c*i.m21,_=o*i.m02+s*i.m12+c*i.m22,v=f*i.m00+p*i.m10+m*i.m20,y=f*i.m01+p*i.m11+m*i.m21,b=f*i.m02+p*i.m12+m*i.m22;return v*=h/l,y*=g/u,b*=_/d,f=v*a.m00+y*a.m10+b*a.m20,p=v*a.m01+y*a.m11+b*a.m21,m=v*a.m02+y*a.m12+b*a.m22,[f,p,m]};O.prototype.lab=function(){return fe(this._rgb)},Object.assign(k,{lab:(...e)=>new O(...e,`lab`),getLabWhitePoint:ce,setLabWhitePoint:M}),D.format.lab=N,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`lab`),h(e)===`array`&&e.length===3)return`lab`}}),O.prototype.darken=function(e=1){let t=this,n=t.lab();return n[0]-=j.Kn*e,new O(n,`lab`).alpha(t.alpha(),!0)},O.prototype.brighten=function(e=1){return this.darken(-e)},O.prototype.darker=O.prototype.darken,O.prototype.brighter=O.prototype.brighten,O.prototype.get=function(e){let[t,n]=e.split(`.`),r=this[t]();if(n){let e=t.indexOf(n)-(t.substr(0,2)===`ok`?2:0);if(e>-1)return r[e];throw Error(`unknown channel ${n} in mode ${t}`)}else return r};var{pow:ge}=Math,_e=1e-7,ve=20;O.prototype.luminance=function(e,t=`rgb`){if(e!==void 0&&h(e)===`number`){if(e===0)return new O([0,0,0,this._rgb[3]],`rgb`);if(e===1)return new O([255,255,255,this._rgb[3]],`rgb`);let n=this.luminance(),r=ve,i=(n,a)=>{let o=n.interpolate(a,.5,t),s=o.luminance();return Math.abs(e-s)<_e||!r--?o:s>e?i(n,o):i(o,a)};return new O([...(n>e?i(new O([0,0,0]),this):i(this,new O([255,255,255]))).rgb(),this._rgb[3]])}return ye(...this._rgb.slice(0,3))};var ye=(e,t,n)=>(e=be(e),t=be(t),n=be(n),.2126*e+.7152*t+.0722*n),be=e=>(e/=255,e<=.03928?e/12.92:ge((e+.055)/1.055,2.4)),P={},F=(e,t,n=.5,...r)=>{let i=r[0]||`lrgb`;if(!P[i]&&!r.length&&(i=Object.keys(P)[0]),!P[i])throw Error(`interpolation mode ${i} is not defined`);return h(e)!==`object`&&(e=new O(e)),h(t)!==`object`&&(t=new O(t)),P[i](e,t,n).alpha(e.alpha()+n*(t.alpha()-e.alpha()))};O.prototype.mix=O.prototype.interpolate=function(e,t=.5,...n){return F(this,e,t,...n)},O.prototype.premultiply=function(e=!1){let t=this._rgb,n=t[3];return e?(this._rgb=[t[0]*n,t[1]*n,t[2]*n,n],this):new O([t[0]*n,t[1]*n,t[2]*n,n],`rgb`)};var{sin:xe,cos:Se}=Math,Ce=(...e)=>{let[t,n,r]=g(e,`lch`);return isNaN(r)&&(r=0),r*=T,[t,Se(r)*n,xe(r)*n]},we=(...e)=>{e=g(e,`lch`);let[t,n,r]=e,[i,a,o]=Ce(t,n,r),[s,c,l]=N(i,a,o);return[s,c,l,e.length>3?e[3]:1]},Te=(...e)=>we(...E(g(e,`hcl`))),{sqrt:Ee,atan2:De,round:Oe}=Math,ke=(...e)=>{let[t,n,r]=g(e,`lab`),i=Ee(n*n+r*r),a=(De(r,n)*ee+360)%360;return Oe(i*1e4)===0&&(a=NaN),[t,i,a]},Ae=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=fe(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};O.prototype.lch=function(){return Ae(this._rgb)},O.prototype.hcl=function(){return E(Ae(this._rgb))},Object.assign(k,{lch:(...e)=>new O(...e,`lch`),hcl:(...e)=>new O(...e,`hcl`)}),D.format.lch=we,D.format.hcl=Te,[`lch`,`hcl`].forEach(e=>D.autodetect.push({p:2,test:(...t)=>{if(t=g(t,e),h(t)===`array`&&t.length===3)return e}})),O.prototype.saturate=function(e=1){let t=this,n=t.lch();return n[1]+=j.Kn*e,n[1]<0&&(n[1]=0),new O(n,`lch`).alpha(t.alpha(),!0)},O.prototype.desaturate=function(e=1){return this.saturate(-e)},O.prototype.set=function(e,t,n=!1){let[r,i]=e.split(`.`),a=this[r]();if(i){let e=r.indexOf(i)-(r.substr(0,2)===`ok`?2:0);if(e>-1){if(h(t)==`string`)switch(t.charAt(0)){case`+`:a[e]+=+t;break;case`-`:a[e]+=+t;break;case`*`:a[e]*=+t.substr(1);break;case`/`:a[e]/=+t.substr(1);break;default:a[e]=+t}else if(h(t)===`number`)a[e]=t;else throw Error(`unsupported value for Color.set`);let i=new O(a,r);return n?(this._rgb=i._rgb,this):i}throw Error(`unknown channel ${i} in mode ${r}`)}else return a},O.prototype.tint=function(e=.5,...t){return F(this,`white`,e,...t)},O.prototype.shade=function(e=.5,...t){return F(this,`black`,e,...t)},P.rgb=(e,t,n)=>{let r=e._rgb,i=t._rgb;return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`rgb`)};var{sqrt:je,pow:Me}=Math;P.lrgb=(e,t,n)=>{let[r,i,a]=e._rgb,[o,s,c]=t._rgb;return new O(je(Me(r,2)*(1-n)+Me(o,2)*n),je(Me(i,2)*(1-n)+Me(s,2)*n),je(Me(a,2)*(1-n)+Me(c,2)*n),`rgb`)},P.lab=(e,t,n)=>{let r=e.lab(),i=t.lab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`lab`)};var Ne=(e,t,n,r)=>{let i,a;r===`hsl`?(i=e.hsl(),a=t.hsl()):r===`hsv`?(i=e.hsv(),a=t.hsv()):r===`hcg`?(i=e.hcg(),a=t.hcg()):r===`hsi`?(i=e.hsi(),a=t.hsi()):r===`lch`||r===`hcl`?(r=`hcl`,i=e.hcl(),a=t.hcl()):r===`oklch`&&(i=e.oklch().reverse(),a=t.oklch().reverse());let o,s,c,l,u,d;(r.substr(0,1)===`h`||r===`oklch`)&&([o,c,u]=i,[s,l,d]=a);let f,p,m,h;return!isNaN(o)&&!isNaN(s)?(h=s>o&&s-o>180?s-(o+360):s180?s+360-o:s-o,p=o+n*h):isNaN(o)?isNaN(s)?p=NaN:(p=s,(u==1||u==0)&&r!=`hsv`&&(f=l)):(p=o,(d==1||d==0)&&r!=`hsv`&&(f=c)),f===void 0&&(f=c+n*(l-c)),m=u+n*(d-u),r===`oklch`?new O([m,f,p],r):new O([p,f,m],r)},Pe=(e,t,n)=>Ne(e,t,n,`lch`);P.lch=Pe,P.hcl=Pe;var Fe=e=>{if(h(e)==`number`&&e>=0&&e<=16777215)return[e>>16,e>>8&255,e&255,1];throw Error(`unknown num color: `+e)},Ie=(...e)=>{let[t,n,r]=g(e,`rgb`);return(t<<16)+(n<<8)+r};O.prototype.num=function(){return Ie(this._rgb)},Object.assign(k,{num:(...e)=>new O(...e,`num`)}),D.format.num=Fe,D.autodetect.push({p:5,test:(...e)=>{if(e.length===1&&h(e[0])===`number`&&e[0]>=0&&e[0]<=16777215)return`num`}}),P.num=(e,t,n)=>{let r=e.num();return new O(r+n*(t.num()-r),`num`)};var{floor:Le}=Math,Re=(...e)=>{e=g(e,`hcg`);let[t,n,r]=e,i,a,o;r*=255;let s=n*255;if(n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Le(t),c=t-e,l=r*(1-n),u=l+s*(1-c),d=l+s*c,f=l+s;switch(e){case 0:[i,a,o]=[f,d,l];break;case 1:[i,a,o]=[u,f,l];break;case 2:[i,a,o]=[l,f,d];break;case 3:[i,a,o]=[l,u,f];break;case 4:[i,a,o]=[d,l,f];break;case 5:[i,a,o]=[f,l,u];break}}return[i,a,o,e.length>3?e[3]:1]},ze=(...e)=>{let[t,n,r]=g(e,`rgb`),i=y(t,n,r),a=b(t,n,r),o=a-i,s=o*100/255,c=i/(255-o)*100,l;return o===0?l=NaN:(t===a&&(l=(n-r)/o),n===a&&(l=2+(r-t)/o),r===a&&(l=4+(t-n)/o),l*=60,l<0&&(l+=360)),[l,s,c]};O.prototype.hcg=function(){return ze(this._rgb)},k.hcg=(...e)=>new O(...e,`hcg`),D.format.hcg=Re,D.autodetect.push({p:1,test:(...e)=>{if(e=g(e,`hcg`),h(e)===`array`&&e.length===3)return`hcg`}}),P.hcg=(e,t,n)=>Ne(e,t,n,`hcg`);var{cos:Be}=Math,Ve=(...e)=>{e=g(e,`hsi`);let[t,n,r]=e,i,a,o;return isNaN(t)&&(t=0),isNaN(n)&&(n=0),t>360&&(t-=360),t<0&&(t+=360),t/=360,t<1/3?(o=(1-n)/3,i=(1+n*Be(C*t)/Be(w-C*t))/3,a=1-(o+i)):t<2/3?(t-=1/3,i=(1-n)/3,a=(1+n*Be(C*t)/Be(w-C*t))/3,o=1-(i+a)):(t-=2/3,a=(1-n)/3,o=(1+n*Be(C*t)/Be(w-C*t))/3,i=1-(a+o)),i=f(r*i*3),a=f(r*a*3),o=f(r*o*3),[i*255,a*255,o*255,e.length>3?e[3]:1]},{min:He,sqrt:Ue,acos:We}=Math,Ge=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i,a=He(t,n,r),o=(t+n+r)/3,s=o>0?1-a/o:0;return s===0?i=NaN:(i=(t-n+(t-r))/2,i/=Ue((t-n)*(t-n)+(t-r)*(n-r)),i=We(i),r>n&&(i=C-i),i/=C),[i*360,s,o]};O.prototype.hsi=function(){return Ge(this._rgb)},k.hsi=(...e)=>new O(...e,`hsi`),D.format.hsi=Ve,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsi`),h(e)===`array`&&e.length===3)return`hsi`}}),P.hsi=(e,t,n)=>Ne(e,t,n,`hsi`);var Ke=(...e)=>{e=g(e,`hsl`);let[t,n,r]=e,i,a,o;if(n===0)i=a=o=r*255;else{let e=[0,0,0],s=[0,0,0],c=r<.5?r*(1+n):r+n-r*n,l=2*r-c,u=t/360;e[0]=u+1/3,e[1]=u,e[2]=u-1/3;for(let t=0;t<3;t++)e[t]<0&&(e[t]+=1),e[t]>1&&--e[t],6*e[t]<1?s[t]=l+(c-l)*6*e[t]:2*e[t]<1?s[t]=c:3*e[t]<2?s[t]=l+(c-l)*(2/3-e[t])*6:s[t]=l;[i,a,o]=[s[0]*255,s[1]*255,s[2]*255]}return e.length>3?[i,a,o,e[3]]:[i,a,o,1]},qe=(...e)=>{e=g(e,`rgba`);let[t,n,r]=e;t/=255,n/=255,r/=255;let i=y(t,n,r),a=b(t,n,r),o=(a+i)/2,s,c;return a===i?(s=0,c=NaN):s=o<.5?(a-i)/(a+i):(a-i)/(2-a-i),t==a?c=(n-r)/(a-i):n==a?c=2+(r-t)/(a-i):r==a&&(c=4+(t-n)/(a-i)),c*=60,c<0&&(c+=360),e.length>3&&e[3]!==void 0?[c,s,o,e[3]]:[c,s,o]};O.prototype.hsl=function(){return qe(this._rgb)},k.hsl=(...e)=>new O(...e,`hsl`),D.format.hsl=Ke,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsl`),h(e)===`array`&&e.length===3)return`hsl`}}),P.hsl=(e,t,n)=>Ne(e,t,n,`hsl`);var{floor:Je}=Math,Ye=(...e)=>{e=g(e,`hsv`);let[t,n,r]=e,i,a,o;if(r*=255,n===0)i=a=o=r;else{t===360&&(t=0),t>360&&(t-=360),t<0&&(t+=360),t/=60;let e=Je(t),s=t-e,c=r*(1-n),l=r*(1-n*s),u=r*(1-n*(1-s));switch(e){case 0:[i,a,o]=[r,u,c];break;case 1:[i,a,o]=[l,r,c];break;case 2:[i,a,o]=[c,r,u];break;case 3:[i,a,o]=[c,l,r];break;case 4:[i,a,o]=[u,c,r];break;case 5:[i,a,o]=[r,c,l];break}}return[i,a,o,e.length>3?e[3]:1]},{min:Xe,max:Ze}=Math,Qe=(...e)=>{e=g(e,`rgb`);let[t,n,r]=e,i=Xe(t,n,r),a=Ze(t,n,r),o=a-i,s,c,l;return l=a/255,a===0?(s=NaN,c=0):(c=o/a,t===a&&(s=(n-r)/o),n===a&&(s=2+(r-t)/o),r===a&&(s=4+(t-n)/o),s*=60,s<0&&(s+=360)),[s,c,l]};O.prototype.hsv=function(){return Qe(this._rgb)},k.hsv=(...e)=>new O(...e,`hsv`),D.format.hsv=Ye,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`hsv`),h(e)===`array`&&e.length===3)return`hsv`}}),P.hsv=(e,t,n)=>Ne(e,t,n,`hsv`);function $e(e,t){let n=e.length;Array.isArray(e[0])||(e=[e]),Array.isArray(t[0])||(t=t.map(e=>[e]));let r=t[0].length,i=t[0].map((e,n)=>t.map(e=>e[n])),a=e.map(e=>i.map(t=>Array.isArray(e)?e.reduce((e,n,r)=>e+n*(t[r]||0),0):t.reduce((t,n)=>t+n*e,0)));return n===1&&(a=a[0]),r===1?a.map(e=>e[0]):a}var et=(...e)=>{e=g(e,`lab`);let[t,n,r,...i]=e,[a,o,s]=tt([t,n,r]),[c,l,u]=de(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]};function tt(e){return $e([[1.2268798758459243,-.5578149944602171,.2813910456659647],[-.0405757452148008,1.112286803280317,-.0717110580655164],[-.0763729366746601,-.4214933324022432,1.5869240198367816]],$e([[1,.3963377773761749,.2158037573099136],[1,-.1055613458156586,-.0638541728258133],[1,-.0894841775298119,-1.2914855480194092]],e).map(e=>e**3))}var nt=(...e)=>{let[t,n,r,...i]=g(e,`rgb`);return[...rt(he(t,n,r)),...i.length>0&&i[0]<1?[i[0]]:[]]};function rt(e){return $e([[.210454268309314,.7936177747023054,-.0040720430116193],[1.9779985324311684,-2.42859224204858,.450593709617411],[.0259040424655478,.7827717124575296,-.8086757549230774]],$e([[.819022437996703,.3619062600528904,-.1288737815209879],[.0329836539323885,.9292868615863434,.0361446663506424],[.0481771893596242,.2642395317527308,.6335478284694309]],e).map(e=>Math.cbrt(e)))}O.prototype.oklab=function(){return nt(this._rgb)},Object.assign(k,{oklab:(...e)=>new O(...e,`oklab`)}),D.format.oklab=et,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklab`),h(e)===`array`&&e.length===3)return`oklab`}}),P.oklab=(e,t,n)=>{let r=e.oklab(),i=t.oklab();return new O(r[0]+n*(i[0]-r[0]),r[1]+n*(i[1]-r[1]),r[2]+n*(i[2]-r[2]),`oklab`)},P.oklch=(e,t,n)=>Ne(e,t,n,`oklch`);var{pow:it,sqrt:at,PI:ot,cos:st,sin:ct,atan2:lt}=Math,ut=(e,t=`lrgb`,n=null)=>{let r=e.length;n||=Array.from(Array(r)).map(()=>1);let i=r/n.reduce(function(e,t){return e+t});if(n.forEach((e,t)=>{n[t]*=i}),e=e.map(e=>new O(e)),t===`lrgb`)return dt(e,n);let a=e.shift(),o=a.get(t),s=[],c=0,l=0;for(let e=0;e{let i=e.get(t);u+=e.alpha()*n[r+1];for(let e=0;e=360;)t-=360;o[e]=t}else o[e]=o[e]/s[e];return u/=r,new O(o,t).alpha(u>.99999?1:u,!0)},dt=(e,t)=>{let n=e.length,r=[0,0,0,0];for(let i=0;i.9999999&&(r[3]=1),new O(p(r))},{pow:ft}=Math;function pt(e){let t=`rgb`,n=k(`#ccc`),r=0,i=[0,1],a=[0,1],o=[],s=[0,0],c=!1,l=[],u=!1,d=0,p=1,m=!1,g={},_=!0,v=1,y=function(e){if(e||=[`#fff`,`#000`],e&&h(e)===`string`&&k.brewer&&k.brewer[e.toLowerCase()]&&(e=k.brewer[e.toLowerCase()]),h(e)===`array`){e.length===1&&(e=[e[0],e[0]]),e=e.slice(0);for(let t=0;t=c[n];)n++;return n-1}return 0},x=e=>e,S=e=>e,C=function(e,r){let i,a;if(r??=!1,isNaN(e)||e===null)return n;a=r?e:c&&c.length>2?b(e)/(c.length-2):p===d?1:(e-d)/(p-d),a=S(a),r||(a=x(a)),v!==1&&(a=ft(a,v)),a=s[0]+a*(1-s[0]-s[1]),a=f(a,0,1);let u=Math.floor(a*1e4);if(_&&g[u])i=g[u];else{if(h(l)===`array`)for(let e=0;e=n&&e===o.length-1){i=l[e];break}if(a>n&&ag={};y(e);let T=function(e){let t=k(C(e));return u&&t[u]?t[u]():t};return T.classes=function(e){if(e!=null){if(h(e)===`array`)c=e,i=[e[0],e[e.length-1]];else{let t=k.analyze(i);c=e===0?[t.min,t.max]:k.limits(t,`e`,e)}return T}return c},T.domain=function(e){if(!arguments.length)return a;a=e.slice(0),d=e[0],p=e[e.length-1],o=[];let t=l.length;if(e.length===t&&d!==p)for(let t of Array.from(e))o.push((t-d)/(p-d));else{for(let e=0;e2){let t=e.map((t,n)=>n/(e.length-1)),n=e.map(e=>(e-d)/(p-d));n.every((e,n)=>t[n]===e)||(S=e=>{if(e<=0||e>=1)return e;let r=0;for(;e>=n[r+1];)r++;let i=(e-n[r])/(n[r+1]-n[r]);return t[r]+i*(t[r+1]-t[r])})}}return i=[d,p],T},T.mode=function(e){return arguments.length?(t=e,w(),T):t},T.range=function(e,t){return y(e,t),T},T.out=function(e){return u=e,T},T.spread=function(e){return arguments.length?(r=e,T):r},T.correctLightness=function(e){return e??=!0,m=e,w(),x=m?function(e){let t=C(0,!0).lab()[0],n=C(1,!0).lab()[0],r=t>n,i=C(e,!0).lab()[0],a=t+(n-t)*e,o=i-a,s=0,c=1,l=20;for(;Math.abs(o)>.01&&l-- >0;)(function(){return r&&(o*=-1),o<0?(s=e,e+=(c-e)*.5):(c=e,e+=(s-e)*.5),i=C(e,!0).lab()[0],o=i-a})();return e}:e=>e,T},T.padding=function(e){return e==null?s:(h(e)===`number`&&(e=[e,e]),s=e,T)},T.colors=function(t,n){arguments.length<2&&(n=`hex`);let r=[];if(arguments.length===0)r=l.slice(0);else if(t===1)r=[T(.5)];else if(t>1){let e=i[0],n=i[1]-e;r=mt(0,t,!1).map(r=>T(e+r/(t-1)*n))}else{e=[];let t=[];if(c&&c.length>2)for(let e=1,n=c.length,r=1<=n;r?en;r?e++:e--)t.push((c[e-1]+c[e])*.5);else t=i;r=t.map(e=>T(e))}return k[n]&&(r=r.map(e=>e[n]())),r},T.cache=function(e){return e==null?_:(_=e,T)},T.gamma=function(e){return e==null?v:(v=e,T)},T.nodata=function(e){return e==null?n:(n=k(e),T)},T}function mt(e,t,n){let r=[],i=ea;i?t++:t--)r.push(t);return r}var ht=function(e){let t=[1,1];for(let n=1;nnew O(e)),e.length===2)[n,r]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>n[t]+e*(r[t]-n[t])),`lab`)};else if(e.length===3)[n,r,i]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*n[t]+2*(1-e)*e*r[t]+e*e*i[t]),`lab`)};else if(e.length===4){let a;[n,r,i,a]=e.map(e=>e.lab()),t=function(e){return new O([0,1,2].map(t=>(1-e)*(1-e)*(1-e)*n[t]+3*(1-e)*(1-e)*e*r[t]+3*(1-e)*e*e*i[t]+e*e*e*a[t]),`lab`)}}else if(e.length>=5){let n,r,i;n=e.map(e=>e.lab()),i=e.length-1,r=ht(i),t=function(e){let t=1-e;return new O([0,1,2].map(a=>n.reduce((n,o,s)=>n+r[s]*t**(i-s)*e**s*o[a],0)),`lab`)}}else throw RangeError(`No point in running bezier with only one color.`);return t},_t=e=>{let t=gt(e);return t.scale=()=>pt(t),t},{round:vt}=Math;O.prototype.rgb=function(e=!0){return e===!1?this._rgb.slice(0,3):this._rgb.slice(0,3).map(vt)},O.prototype.rgba=function(e=!0){return this._rgb.slice(0,4).map((t,n)=>n<3?e===!1?t:vt(t):t)},Object.assign(k,{rgb:(...e)=>new O(...e,`rgb`)}),D.format.rgb=(...e)=>{let t=g(e,`rgba`);return t[3]===void 0&&(t[3]=1),t},D.autodetect.push({p:3,test:(...e)=>{if(e=g(e,`rgba`),h(e)===`array`&&(e.length===3||e.length===4&&h(e[3])==`number`&&e[3]>=0&&e[3]<=1))return`rgb`}});var I=(e,t,n)=>{if(!I[n])throw Error(`unknown blend mode `+n);return I[n](e,t)},L=e=>(t,n)=>{let r=k(n).rgb(),i=k(t).rgb();return k.rgb(e(r,i))},R=e=>(t,n)=>{let r=[];return r[0]=e(t[0],n[0]),r[1]=e(t[1],n[1]),r[2]=e(t[2],n[2]),r};I.normal=L(R(e=>e)),I.multiply=L(R((e,t)=>e*t/255)),I.screen=L(R((e,t)=>255*(1-(1-e/255)*(1-t/255)))),I.overlay=L(R((e,t)=>t<128?2*e*t/255:255*(1-2*(1-e/255)*(1-t/255)))),I.darken=L(R((e,t)=>e>t?t:e)),I.lighten=L(R((e,t)=>e>t?e:t)),I.dodge=L(R((e,t)=>e===255?255:(e=t/255*255/(1-e/255),e>255?255:e))),I.burn=L(R((e,t)=>255*(1-(1-t/255)/(e/255))));var{pow:yt,sin:bt,cos:xt}=Math;function St(e=300,t=-1.5,n=1,r=1,i=[0,1]){let a=0,o;h(i)===`array`?o=i[1]-i[0]:(o=0,i=[i,i]);let s=function(s){let c=C*((e+120)/360+t*s),l=yt(i[0]+o*s,r),u=(a===0?n:n[0]+s*a)*l*(1-l)/2,d=xt(c),f=bt(c),m=l+u*(-.14861*d+1.78277*f),h=l+u*(-.29227*d-.90649*f),g=l+1.97294*d*u;return k(p([m*255,h*255,g*255,1]))};return s.start=function(t){return t==null?e:(e=t,s)},s.rotations=function(e){return e==null?t:(t=e,s)},s.gamma=function(e){return e==null?r:(r=e,s)},s.hue=function(e){return e==null?n:(n=e,h(n)===`array`?(a=n[1]-n[0],a===0&&(n=n[1])):a=0,s)},s.lightness=function(e){return e==null?i:(h(e)===`array`?(i=e,o=e[1]-e[0]):(i=[e,e],o=0),s)},s.scale=()=>k.scale(s),s.hue(n),s}var Ct=`0123456789abcdef`,{floor:wt,random:Tt}=Math,Et=(e=Tt)=>{let t=`#`;for(let n=0;n<6;n++)t+=Ct.charAt(wt(e()*16));return new O(t,`hex`)},{log:Dt,pow:Ot,floor:kt,abs:At}=Math;function jt(e,t=null){let n={min:Number.MAX_VALUE,max:Number.MAX_VALUE*-1,sum:0,values:[],count:0};return h(e)===`object`&&(e=Object.values(e)),e.forEach(e=>{t&&h(e)===`object`&&(e=e[t]),e!=null&&!isNaN(e)&&(n.values.push(e),n.sum+=e,en.max&&(n.max=e),n.count+=1)}),n.domain=[n.min,n.max],n.limits=(e,t)=>Mt(n,e,t),n}function Mt(e,t=`equal`,n=7){h(e)==`array`&&(e=jt(e));let{min:r,max:i}=e,a=e.values.sort((e,t)=>e-t);if(n===1)return[r,i];let o=[];if(t.substr(0,1)===`c`&&(o.push(r),o.push(i)),t.substr(0,1)===`e`){o.push(r);for(let e=1;e 0`);let e=Math.LOG10E*Dt(r),t=Math.LOG10E*Dt(i);o.push(r);for(let r=1;r200&&(l=!1)}let f={};for(let e=0;ee-t),o.push(p[0]);for(let e=1;e{e=new O(e),t=new O(t);let n=e.luminance(),r=t.luminance();return n>r?(n+.05)/(r+.05):(r+.05)/(n+.05)},Pt=.027,Ft=5e-4,It=.1,Lt=1.14,Rt=.022,zt=1.414,Bt=(e,t)=>{e=new O(e),t=new O(t),e.alpha()<1&&(e=F(t,e,e.alpha(),`rgb`));let n=Vt(...e.rgb()),r=Vt(...t.rgb()),i=n>=Rt?n:n+(Rt-n)**+zt,a=r>=Rt?r:r+(Rt-r)**+zt,o=a**.56-i**.57,s=a**.65-i**.62,c=Math.abs(a-i)0?c-Pt:c+Pt)*100};function Vt(e,t,n){return .2126729*(e/255)**2.4+.7151522*(t/255)**2.4+.072175*(n/255)**2.4}var{sqrt:z,pow:B,min:Ht,max:Ut,atan2:Wt,abs:Gt,cos:Kt,sin:qt,exp:Jt,PI:Yt}=Math;function Xt(e,t,n=1,r=1,i=1){var a=function(e){return 360*e/(2*Yt)},o=function(e){return 2*Yt*e/360};e=new O(e),t=new O(t);let[s,c,l]=Array.from(e.lab()),[u,d,f]=Array.from(t.lab()),p=(s+u)/2,m=(z(B(c,2)+B(l,2))+z(B(d,2)+B(f,2)))/2,h=.5*(1-z(B(m,7)/(B(m,7)+B(25,7)))),g=c*(1+h),_=d*(1+h),v=z(B(g,2)+B(l,2)),y=z(B(_,2)+B(f,2)),b=(v+y)/2,x=a(Wt(l,g)),S=a(Wt(f,_)),C=x>=0?x:x+360,w=S>=0?S:S+360,T=Gt(C-w)>180?(C+w+360)/2:(C+w)/2,ee=1-.17*Kt(o(T-30))+.24*Kt(o(2*T))+.32*Kt(o(3*T+6))-.2*Kt(o(4*T-63)),E=w-C;E=Gt(E)<=180?E:w<=C?E+360:E-360,E=2*z(v*y)*qt(o(E)/2);let D=u-s,te=y-v,k=1+.015*B(p-50,2)/z(20+B(p-50,2)),A=1+.045*b,ne=1+.015*b*ee,re=30*Jt(-B((T-275)/25,2)),ie=-(2*z(B(b,7)/(B(b,7)+B(25,7))))*qt(2*o(re));return Ut(0,Ht(100,z(B(D/(n*k),2)+B(te/(r*A),2)+B(E/(i*ne),2)+ie*(te/(r*A))*(E/(i*ne)))))}function Zt(e,t,n=`lab`){e=new O(e),t=new O(t);let r=e.get(n),i=t.get(n),a=0;for(let e in r){let t=(r[e]||0)-(i[e]||0);a+=t*t}return Math.sqrt(a)}var Qt=(...e)=>{try{return new O(...e),!0}catch{return!1}},$t={cool(){return pt([k.hsl(180,1,.9),k.hsl(250,.7,.4)])},hot(){return pt([`#000`,`#f00`,`#ff0`,`#fff`],[0,.25,.75,1]).mode(`rgb`)}},en={OrRd:[`#fff7ec`,`#fee8c8`,`#fdd49e`,`#fdbb84`,`#fc8d59`,`#ef6548`,`#d7301f`,`#b30000`,`#7f0000`],PuBu:[`#fff7fb`,`#ece7f2`,`#d0d1e6`,`#a6bddb`,`#74a9cf`,`#3690c0`,`#0570b0`,`#045a8d`,`#023858`],BuPu:[`#f7fcfd`,`#e0ecf4`,`#bfd3e6`,`#9ebcda`,`#8c96c6`,`#8c6bb1`,`#88419d`,`#810f7c`,`#4d004b`],Oranges:[`#fff5eb`,`#fee6ce`,`#fdd0a2`,`#fdae6b`,`#fd8d3c`,`#f16913`,`#d94801`,`#a63603`,`#7f2704`],BuGn:[`#f7fcfd`,`#e5f5f9`,`#ccece6`,`#99d8c9`,`#66c2a4`,`#41ae76`,`#238b45`,`#006d2c`,`#00441b`],YlOrBr:[`#ffffe5`,`#fff7bc`,`#fee391`,`#fec44f`,`#fe9929`,`#ec7014`,`#cc4c02`,`#993404`,`#662506`],YlGn:[`#ffffe5`,`#f7fcb9`,`#d9f0a3`,`#addd8e`,`#78c679`,`#41ab5d`,`#238443`,`#006837`,`#004529`],Reds:[`#fff5f0`,`#fee0d2`,`#fcbba1`,`#fc9272`,`#fb6a4a`,`#ef3b2c`,`#cb181d`,`#a50f15`,`#67000d`],RdPu:[`#fff7f3`,`#fde0dd`,`#fcc5c0`,`#fa9fb5`,`#f768a1`,`#dd3497`,`#ae017e`,`#7a0177`,`#49006a`],Greens:[`#f7fcf5`,`#e5f5e0`,`#c7e9c0`,`#a1d99b`,`#74c476`,`#41ab5d`,`#238b45`,`#006d2c`,`#00441b`],YlGnBu:[`#ffffd9`,`#edf8b1`,`#c7e9b4`,`#7fcdbb`,`#41b6c4`,`#1d91c0`,`#225ea8`,`#253494`,`#081d58`],Purples:[`#fcfbfd`,`#efedf5`,`#dadaeb`,`#bcbddc`,`#9e9ac8`,`#807dba`,`#6a51a3`,`#54278f`,`#3f007d`],GnBu:[`#f7fcf0`,`#e0f3db`,`#ccebc5`,`#a8ddb5`,`#7bccc4`,`#4eb3d3`,`#2b8cbe`,`#0868ac`,`#084081`],Greys:[`#ffffff`,`#f0f0f0`,`#d9d9d9`,`#bdbdbd`,`#969696`,`#737373`,`#525252`,`#252525`,`#000000`],YlOrRd:[`#ffffcc`,`#ffeda0`,`#fed976`,`#feb24c`,`#fd8d3c`,`#fc4e2a`,`#e31a1c`,`#bd0026`,`#800026`],PuRd:[`#f7f4f9`,`#e7e1ef`,`#d4b9da`,`#c994c7`,`#df65b0`,`#e7298a`,`#ce1256`,`#980043`,`#67001f`],Blues:[`#f7fbff`,`#deebf7`,`#c6dbef`,`#9ecae1`,`#6baed6`,`#4292c6`,`#2171b5`,`#08519c`,`#08306b`],PuBuGn:[`#fff7fb`,`#ece2f0`,`#d0d1e6`,`#a6bddb`,`#67a9cf`,`#3690c0`,`#02818a`,`#016c59`,`#014636`],Viridis:[`#440154`,`#482777`,`#3f4a8a`,`#31678e`,`#26838f`,`#1f9d8a`,`#6cce5a`,`#b6de2b`,`#fee825`],Spectral:[`#9e0142`,`#d53e4f`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#e6f598`,`#abdda4`,`#66c2a5`,`#3288bd`,`#5e4fa2`],RdYlGn:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee08b`,`#ffffbf`,`#d9ef8b`,`#a6d96a`,`#66bd63`,`#1a9850`,`#006837`],RdBu:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#f7f7f7`,`#d1e5f0`,`#92c5de`,`#4393c3`,`#2166ac`,`#053061`],PiYG:[`#8e0152`,`#c51b7d`,`#de77ae`,`#f1b6da`,`#fde0ef`,`#f7f7f7`,`#e6f5d0`,`#b8e186`,`#7fbc41`,`#4d9221`,`#276419`],PRGn:[`#40004b`,`#762a83`,`#9970ab`,`#c2a5cf`,`#e7d4e8`,`#f7f7f7`,`#d9f0d3`,`#a6dba0`,`#5aae61`,`#1b7837`,`#00441b`],RdYlBu:[`#a50026`,`#d73027`,`#f46d43`,`#fdae61`,`#fee090`,`#ffffbf`,`#e0f3f8`,`#abd9e9`,`#74add1`,`#4575b4`,`#313695`],BrBG:[`#543005`,`#8c510a`,`#bf812d`,`#dfc27d`,`#f6e8c3`,`#f5f5f5`,`#c7eae5`,`#80cdc1`,`#35978f`,`#01665e`,`#003c30`],RdGy:[`#67001f`,`#b2182b`,`#d6604d`,`#f4a582`,`#fddbc7`,`#ffffff`,`#e0e0e0`,`#bababa`,`#878787`,`#4d4d4d`,`#1a1a1a`],PuOr:[`#7f3b08`,`#b35806`,`#e08214`,`#fdb863`,`#fee0b6`,`#f7f7f7`,`#d8daeb`,`#b2abd2`,`#8073ac`,`#542788`,`#2d004b`],Set2:[`#66c2a5`,`#fc8d62`,`#8da0cb`,`#e78ac3`,`#a6d854`,`#ffd92f`,`#e5c494`,`#b3b3b3`],Accent:[`#7fc97f`,`#beaed4`,`#fdc086`,`#ffff99`,`#386cb0`,`#f0027f`,`#bf5b17`,`#666666`],Set1:[`#e41a1c`,`#377eb8`,`#4daf4a`,`#984ea3`,`#ff7f00`,`#ffff33`,`#a65628`,`#f781bf`,`#999999`],Set3:[`#8dd3c7`,`#ffffb3`,`#bebada`,`#fb8072`,`#80b1d3`,`#fdb462`,`#b3de69`,`#fccde5`,`#d9d9d9`,`#bc80bd`,`#ccebc5`,`#ffed6f`],Dark2:[`#1b9e77`,`#d95f02`,`#7570b3`,`#e7298a`,`#66a61e`,`#e6ab02`,`#a6761d`,`#666666`],Paired:[`#a6cee3`,`#1f78b4`,`#b2df8a`,`#33a02c`,`#fb9a99`,`#e31a1c`,`#fdbf6f`,`#ff7f00`,`#cab2d6`,`#6a3d9a`,`#ffff99`,`#b15928`],Pastel2:[`#b3e2cd`,`#fdcdac`,`#cbd5e8`,`#f4cae4`,`#e6f5c9`,`#fff2ae`,`#f1e2cc`,`#cccccc`],Pastel1:[`#fbb4ae`,`#b3cde3`,`#ccebc5`,`#decbe4`,`#fed9a6`,`#ffffcc`,`#e5d8bd`,`#fddaec`,`#f2f2f2`]},tn=Object.keys(en),nn=new Map(tn.map(e=>[e.toLowerCase(),e])),rn=typeof Proxy==`function`?new Proxy(en,{get(e,t){let n=t.toLowerCase();if(nn.has(n))return e[nn.get(n)]},getOwnPropertyNames(){return Object.getOwnPropertyNames(tn)}}):en,an=(...e)=>{e=g(e,`cmyk`);let[t,n,r,i]=e,a=e.length>4?e[4]:1;return i===1?[0,0,0,a]:[t>=1?0:255*(1-t)*(1-i),n>=1?0:255*(1-n)*(1-i),r>=1?0:255*(1-r)*(1-i),a]},{max:on}=Math,sn=(...e)=>{let[t,n,r]=g(e,`rgb`);t/=255,n/=255,r/=255;let i=1-on(t,on(n,r)),a=i<1?1/(1-i):0;return[(1-t-i)*a,(1-n-i)*a,(1-r-i)*a,i]};O.prototype.cmyk=function(){return sn(this._rgb)},Object.assign(k,{cmyk:(...e)=>new O(...e,`cmyk`)}),D.format.cmyk=an,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`cmyk`),h(e)===`array`&&e.length===4)return`cmyk`}});var cn=(...e)=>{let t=g(e,`hsla`),n=_(e)||`lsa`;return t[0]=x(t[0]||0)+`deg`,t[1]=x(t[1]*100)+`%`,t[2]=x(t[2]*100)+`%`,n===`hsla`||t.length>3&&t[3]<1?(t[3]=`/ `+(t.length>3?t[3]:1),n=`hsla`):t.length=3,`${n.substr(0,3)}(${t.join(` `)})`},ln=(...e)=>{let t=g(e,`lab`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=x(t[2]),n===`laba`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lab(${t.join(` `)})`},un=(...e)=>{let t=g(e,`lch`),n=_(e)||`lab`;return t[0]=x(t[0])+`%`,t[1]=x(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,n===`lcha`||t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`lch(${t.join(` `)})`},dn=(...e)=>{let t=g(e,`lab`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=S(t[2]),t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklab(${t.join(` `)})`},fn=(...e)=>{let[t,n,r,...i]=g(e,`rgb`),[a,o,s]=nt(t,n,r),[c,l,u]=ke(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},pn=(...e)=>{let t=g(e,`lch`);return t[0]=x(t[0]*100)+`%`,t[1]=S(t[1]),t[2]=isNaN(t[2])?`none`:x(t[2])+`deg`,t.length>3&&t[3]<1?t[3]=`/ `+(t.length>3?t[3]:1):t.length=3,`oklch(${t.join(` `)})`},{round:mn}=Math,hn=(...e)=>{let t=g(e,`rgba`),n=_(e)||`rgb`;if(n.substr(0,3)===`hsl`)return cn(qe(t),n);if(n.substr(0,3)===`lab`){let e=ce();M(`d50`);let r=ln(fe(t),n);return M(e),r}if(n.substr(0,3)===`lch`){let e=ce();M(`d50`);let r=un(Ae(t),n);return M(e),r}return n.substr(0,5)===`oklab`?dn(nt(t)):n.substr(0,5)===`oklch`?pn(fn(t)):(t[0]=mn(t[0]),t[1]=mn(t[1]),t[2]=mn(t[2]),(n===`rgba`||t.length>3&&t[3]<1)&&(t[3]=`/ `+(t.length>3?t[3]:1),n=`rgba`),`${n.substr(0,3)}(${t.slice(0,n===`rgb`?3:4).join(` `)})`)},gn=(...e)=>{e=g(e,`lch`);let[t,n,r,...i]=e,[a,o,s]=Ce(t,n,r),[c,l,u]=et(a,o,s);return[c,l,u,...i.length>0&&i[0]<1?[i[0]]:[]]},V=`((?:-?\\d+)|(?:-?\\d+(?:\\.\\d+)?)%|none)`,H=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%?)|none)`,_n=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)%)|none)`,U=`\\s*`,vn=`\\s+`,yn=`\\s*,\\s*`,bn=`((?:-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:deg)?)|none)`,xn=`\\s*(?:\\/\\s*((?:[01]|[01]?\\.\\d+)|\\d+(?:\\.\\d+)?%))?`,Sn=RegExp(`^rgba?\\(`+U+[V,V,V].join(vn)+xn+`\\)$`),Cn=RegExp(`^rgb\\(`+U+[V,V,V].join(yn)+U+`\\)$`),wn=RegExp(`^rgba\\(`+U+[V,V,V,H].join(yn)+U+`\\)$`),Tn=RegExp(`^hsla?\\(`+U+[bn,_n,_n].join(vn)+xn+`\\)$`),En=RegExp(`^hsl?\\(`+U+[bn,_n,_n].join(yn)+U+`\\)$`),Dn=/^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/,On=RegExp(`^lab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),kn=RegExp(`^lch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),An=RegExp(`^oklab\\(`+U+[H,H,H].join(vn)+xn+`\\)$`),jn=RegExp(`^oklch\\(`+U+[H,H,bn].join(vn)+xn+`\\)$`),{round:Mn}=Math,Nn=e=>e.map((e,t)=>t<=2?f(Mn(e),0,255):e),W=(e,t=0,n=100,r=!1)=>(typeof e==`string`&&e.endsWith(`%`)&&(e=parseFloat(e.substring(0,e.length-1))/100,e=r?t+(e+1)*.5*(n-t):t+e*(n-t)),+e),G=(e,t)=>e===`none`?t:e,Pn=e=>{if(e=e.toLowerCase().trim(),e===`transparent`)return[0,0,0,0];let t;if(D.format.named)try{return D.format.named(e)}catch{}if((t=e.match(Sn))||(t=e.match(Cn))){let e=t.slice(1,4);for(let t=0;t<3;t++)e[t]=+W(G(e[t],0),0,255);e=Nn(e);let n=t[4]===void 0?1:+W(t[4],0,1);return e[3]=n,e}if(t=e.match(wn)){let e=t.slice(1,5);for(let t=0;t<4;t++)e[t]=+W(e[t],0,255);return e}if((t=e.match(Tn))||(t=e.match(En))){let e=t.slice(1,4);e[0]=+G(e[0].replace(`deg`,``),0),e[1]=W(G(e[1],0),0,100)*.01,e[2]=W(G(e[2],0),0,100)*.01;let n=Nn(Ke(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(Dn)){let e=t.slice(1,4);e[1]*=.01,e[2]*=.01;let n=Ke(e);for(let e=0;e<3;e++)n[e]=Mn(n[e]);return n[3]=+t[4],n}if(t=e.match(On)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,100),e[1]=W(G(e[1],0),-125,125,!0),e[2]=W(G(e[2],0),-125,125,!0);let n=ce();M(`d50`);let r=Nn(N(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(kn)){let e=t.slice(1,4);e[0]=W(e[0],0,100),e[1]=W(G(e[1],0),0,150,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=ce();M(`d50`);let r=Nn(we(e));return M(n),r[3]=t[4]===void 0?1:+W(t[4],0,1),r}if(t=e.match(An)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),-.4,.4,!0),e[2]=W(G(e[2],0),-.4,.4,!0);let n=Nn(et(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}if(t=e.match(jn)){let e=t.slice(1,4);e[0]=W(G(e[0],0),0,1),e[1]=W(G(e[1],0),0,.4,!1),e[2]=+G(e[2].replace(`deg`,``),0);let n=Nn(gn(e));return n[3]=t[4]===void 0?1:+W(t[4],0,1),n}};Pn.test=e=>Sn.test(e)||Tn.test(e)||On.test(e)||kn.test(e)||An.test(e)||jn.test(e)||Cn.test(e)||wn.test(e)||En.test(e)||Dn.test(e)||e===`transparent`,O.prototype.css=function(e){return hn(this._rgb,e)},k.css=(...e)=>new O(...e,`css`),D.format.css=Pn,D.autodetect.push({p:5,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&Pn.test(e))return`css`}}),D.format.gl=(...e)=>{let t=g(e,`rgba`);return t[0]*=255,t[1]*=255,t[2]*=255,t},k.gl=(...e)=>new O(...e,`gl`),O.prototype.gl=function(){let e=this._rgb;return[e[0]/255,e[1]/255,e[2]/255,e[3]]},O.prototype.hex=function(e){return oe(this._rgb,e)},k.hex=(...e)=>new O(...e,`hex`),D.format.hex=ie,D.autodetect.push({p:4,test:(e,...t)=>{if(!t.length&&h(e)===`string`&&[3,4,5,6,7,8,9].indexOf(e.length)>=0)return`hex`}});var{log:Fn}=Math,In=e=>{let t=e/100,n,r,i;return t<66?(n=255,r=t<6?0:-155.25485562709179-.44596950469579133*(r=t-2)+104.49216199393888*Fn(r),i=t<20?0:-254.76935184120902+.8274096064007395*(i=t-10)+115.67994401066147*Fn(i)):(n=351.97690566805693+.114206453784165*(n=t-55)-40.25366309332127*Fn(n),r=325.4494125711974+.07943456536662342*(r=t-50)-28.0852963507957*Fn(r),i=255),[n,r,i,1]},{round:Ln}=Math,Rn=(...e)=>{let t=g(e,`rgb`),n=t[0],r=t[2],i=1e3,a=4e4,o;for(;a-i>.4;){o=(a+i)*.5;let e=In(o);e[2]/e[0]>=r/n?a=o:i=o}return Ln(o)};O.prototype.temp=O.prototype.kelvin=O.prototype.temperature=function(){return Rn(this._rgb)};var zn=(...e)=>new O(...e,`temp`);Object.assign(k,{temp:zn,kelvin:zn,temperature:zn}),D.format.temp=D.format.kelvin=D.format.temperature=In,O.prototype.oklch=function(){return fn(this._rgb)},Object.assign(k,{oklch:(...e)=>new O(...e,`oklch`)}),D.format.oklch=gn,D.autodetect.push({p:2,test:(...e)=>{if(e=g(e,`oklch`),h(e)===`array`&&e.length===3)return`oklch`}}),Object.assign(k,{analyze:jt,average:ut,bezier:_t,blend:I,brewer:rn,Color:O,colors:A,contrast:Nt,contrastAPCA:Bt,cubehelix:St,deltaE:Xt,distance:Zt,input:D,interpolate:F,limits:Mt,mix:F,random:Et,scale:pt,scales:$t,valid:Qt});var K=k,q=class e{constructor(){this.hex=`#000000`,this.rgb_r=0,this.rgb_g=0,this.rgb_b=0,this.xyz_x=0,this.xyz_y=0,this.xyz_z=0,this.luv_l=0,this.luv_u=0,this.luv_v=0,this.lch_l=0,this.lch_c=0,this.lch_h=0,this.hsluv_h=0,this.hsluv_s=0,this.hsluv_l=0,this.hpluv_h=0,this.hpluv_p=0,this.hpluv_l=0,this.r0s=0,this.r0i=0,this.r1s=0,this.r1i=0,this.g0s=0,this.g0i=0,this.g1s=0,this.g1i=0,this.b0s=0,this.b0i=0,this.b1s=0,this.b1i=0}static fromLinear(e){return e<=.0031308?12.92*e:1.055*e**(1/2.4)-.055}static toLinear(e){return e>.04045?((e+.055)/1.055)**2.4:e/12.92}static yToL(t){return t<=e.epsilon?t/e.refY*e.kappa:116*(t/e.refY)**(1/3)-16}static lToY(t){return t<=8?e.refY*t/e.kappa:e.refY*((t+16)/116)**3}static rgbChannelToHex(t){let n=Math.round(t*255),r=n%16,i=(n-r)/16|0;return e.hexChars.charAt(i)+e.hexChars.charAt(r)}static hexToRgbChannel(t,n){let r=e.hexChars.indexOf(t.charAt(n)),i=e.hexChars.indexOf(t.charAt(n+1));return(r*16+i)/255}static distanceFromOriginAngle(e,t,n){let r=t/(Math.sin(n)-e*Math.cos(n));return r<0?1/0:r}static distanceFromOrigin(e,t){return Math.abs(t)/Math.sqrt(e**2+1)}static min6(e,t,n,r,i,a){return Math.min(e,Math.min(t,Math.min(n,Math.min(r,Math.min(i,a)))))}rgbToHex(){this.hex=`#`,this.hex+=e.rgbChannelToHex(this.rgb_r),this.hex+=e.rgbChannelToHex(this.rgb_g),this.hex+=e.rgbChannelToHex(this.rgb_b)}hexToRgb(){this.hex=this.hex.toLowerCase(),this.rgb_r=e.hexToRgbChannel(this.hex,1),this.rgb_g=e.hexToRgbChannel(this.hex,3),this.rgb_b=e.hexToRgbChannel(this.hex,5)}xyzToRgb(){this.rgb_r=e.fromLinear(e.m_r0*this.xyz_x+e.m_r1*this.xyz_y+e.m_r2*this.xyz_z),this.rgb_g=e.fromLinear(e.m_g0*this.xyz_x+e.m_g1*this.xyz_y+e.m_g2*this.xyz_z),this.rgb_b=e.fromLinear(e.m_b0*this.xyz_x+e.m_b1*this.xyz_y+e.m_b2*this.xyz_z)}rgbToXyz(){let t=e.toLinear(this.rgb_r),n=e.toLinear(this.rgb_g),r=e.toLinear(this.rgb_b);this.xyz_x=.41239079926595*t+.35758433938387*n+.18048078840183*r,this.xyz_y=.21263900587151*t+.71516867876775*n+.072192315360733*r,this.xyz_z=.019330818715591*t+.11919477979462*n+.95053215224966*r}xyzToLuv(){let t=this.xyz_x+15*this.xyz_y+3*this.xyz_z,n=4*this.xyz_x,r=9*this.xyz_y;t===0?(n=NaN,r=NaN):(n/=t,r/=t),this.luv_l=e.yToL(this.xyz_y),this.luv_l===0?(this.luv_u=0,this.luv_v=0):(this.luv_u=13*this.luv_l*(n-e.refU),this.luv_v=13*this.luv_l*(r-e.refV))}luvToXyz(){if(this.luv_l===0){this.xyz_x=0,this.xyz_y=0,this.xyz_z=0;return}let t=this.luv_u/(13*this.luv_l)+e.refU,n=this.luv_v/(13*this.luv_l)+e.refV;this.xyz_y=e.lToY(this.luv_l),this.xyz_x=0-9*this.xyz_y*t/((t-4)*n-t*n),this.xyz_z=(9*this.xyz_y-15*n*this.xyz_y-n*this.xyz_x)/(3*n)}luvToLch(){if(this.lch_l=this.luv_l,this.lch_c=Math.sqrt(this.luv_u*this.luv_u+this.luv_v*this.luv_v),this.lch_c<1e-8)this.lch_h=0;else{let e=Math.atan2(this.luv_v,this.luv_u);this.lch_h=e*180/Math.PI,this.lch_h<0&&(this.lch_h=360+this.lch_h)}}lchToLuv(){let e=this.lch_h/180*Math.PI;this.luv_l=this.lch_l,this.luv_u=Math.cos(e)*this.lch_c,this.luv_v=Math.sin(e)*this.lch_c}calculateBoundingLines(t){let n=(t+16)**3/1560896,r=n>e.epsilon?n:t/e.kappa,i=r*(284517*e.m_r0-94839*e.m_r2),a=r*(838422*e.m_r2+769860*e.m_r1+731718*e.m_r0),o=r*(632260*e.m_r2-126452*e.m_r1),s=r*(284517*e.m_g0-94839*e.m_g2),c=r*(838422*e.m_g2+769860*e.m_g1+731718*e.m_g0),l=r*(632260*e.m_g2-126452*e.m_g1),u=r*(284517*e.m_b0-94839*e.m_b2),d=r*(838422*e.m_b2+769860*e.m_b1+731718*e.m_b0),f=r*(632260*e.m_b2-126452*e.m_b1);this.r0s=i/o,this.r0i=a*t/o,this.r1s=i/(o+126452),this.r1i=(a-769860)*t/(o+126452),this.g0s=s/l,this.g0i=c*t/l,this.g1s=s/(l+126452),this.g1i=(c-769860)*t/(l+126452),this.b0s=u/f,this.b0i=d*t/f,this.b1s=u/(f+126452),this.b1i=(d-769860)*t/(f+126452)}calcMaxChromaHpluv(){let t=e.distanceFromOrigin(this.r0s,this.r0i),n=e.distanceFromOrigin(this.r1s,this.r1i),r=e.distanceFromOrigin(this.g0s,this.g0i),i=e.distanceFromOrigin(this.g1s,this.g1i),a=e.distanceFromOrigin(this.b0s,this.b0i),o=e.distanceFromOrigin(this.b1s,this.b1i);return e.min6(t,n,r,i,a,o)}calcMaxChromaHsluv(t){let n=t/360*Math.PI*2,r=e.distanceFromOriginAngle(this.r0s,this.r0i,n),i=e.distanceFromOriginAngle(this.r1s,this.r1i,n),a=e.distanceFromOriginAngle(this.g0s,this.g0i,n),o=e.distanceFromOriginAngle(this.g1s,this.g1i,n),s=e.distanceFromOriginAngle(this.b0s,this.b0i,n),c=e.distanceFromOriginAngle(this.b1s,this.b1i,n);return e.min6(r,i,a,o,s,c)}hsluvToLch(){if(this.hsluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hsluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hsluv_l,this.calculateBoundingLines(this.hsluv_l);let e=this.calcMaxChromaHsluv(this.hsluv_h);this.lch_c=e/100*this.hsluv_s}this.lch_h=this.hsluv_h}lchToHsluv(){if(this.lch_l>99.9999999)this.hsluv_s=0,this.hsluv_l=100;else if(this.lch_l<1e-8)this.hsluv_s=0,this.hsluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHsluv(this.lch_h);this.hsluv_s=this.lch_c/e*100,this.hsluv_l=this.lch_l}this.hsluv_h=this.lch_h}hpluvToLch(){if(this.hpluv_l>99.9999999)this.lch_l=100,this.lch_c=0;else if(this.hpluv_l<1e-8)this.lch_l=0,this.lch_c=0;else{this.lch_l=this.hpluv_l,this.calculateBoundingLines(this.hpluv_l);let e=this.calcMaxChromaHpluv();this.lch_c=e/100*this.hpluv_p}this.lch_h=this.hpluv_h}lchToHpluv(){if(this.lch_l>99.9999999)this.hpluv_p=0,this.hpluv_l=100;else if(this.lch_l<1e-8)this.hpluv_p=0,this.hpluv_l=0;else{this.calculateBoundingLines(this.lch_l);let e=this.calcMaxChromaHpluv();this.hpluv_p=this.lch_c/e*100,this.hpluv_l=this.lch_l}this.hpluv_h=this.lch_h}hsluvToRgb(){this.hsluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hpluvToRgb(){this.hpluvToLch(),this.lchToLuv(),this.luvToXyz(),this.xyzToRgb()}hsluvToHex(){this.hsluvToRgb(),this.rgbToHex()}hpluvToHex(){this.hpluvToRgb(),this.rgbToHex()}rgbToHsluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHsluv()}rgbToHpluv(){this.rgbToXyz(),this.xyzToLuv(),this.luvToLch(),this.lchToHpluv(),this.lchToHpluv()}hexToHsluv(){this.hexToRgb(),this.rgbToHsluv()}hexToHpluv(){this.hexToRgb(),this.rgbToHpluv()}};q.hexChars=`0123456789abcdef`,q.refY=1,q.refU=.19783000664283,q.refV=.46831999493879,q.kappa=903.2962962,q.epsilon=.0088564516,q.m_r0=3.240969941904521,q.m_r1=-1.537383177570093,q.m_r2=-.498610760293,q.m_g0=-.96924363628087,q.m_g1=1.87596750150772,q.m_g2=.041555057407175,q.m_b0=.055630079696993,q.m_b1=-.20397695888897,q.m_b2=1.056971514242878;var Bn=s(((e,t)=>{function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.exports=n})),Vn=s(((e,t)=>{var n=Bn(),r,i;function a(){for(var e in i=[`toString`,`toLocaleString`,`valueOf`,`hasOwnProperty`,`isPrototypeOf`,`propertyIsEnumerable`,`constructor`],r=!0,{toString:null})r=!1}function o(e,t,o){var c,l=0;for(c in r??a(),e)if(s(t,e,c,o)===!1)break;if(r)for(var u=e.constructor,d=!!u&&e===u.prototype;(c=i[l++])&&!((c!==`constructor`||!d&&n(e,c))&&e[c]!==Object.prototype[c]&&s(t,e,c,o)===!1););}function s(e,t,n,r){return e.call(r,t[n],n,t)}t.exports=o})),Hn=s(((e,t)=>{var n=Vn();function r(e){var t=[];return n(e,function(e,n){typeof e==`function`&&t.push(n)}),t.sort()}t.exports=r})),Un=s(((e,t)=>{function n(e,t,n){var r=e.length;t=t==null?0:t<0?Math.max(r+t,0):Math.min(t,r),n=n==null?r:n<0?Math.max(r+n,0):Math.min(n,r);for(var i=[];t{var n=Un();function r(e,t,r){var i=n(arguments,2);return function(){return e.apply(t,i.concat(n(arguments)))}}t.exports=r})),Gn=s(((e,t)=>{function n(e,t,n){if(e!=null)for(var r=-1,i=e.length;++r{var n=Hn(),r=Wn(),i=Gn(),a=Un();function o(e,t){i(arguments.length>1?a(arguments,1):n(e),function(t){e[t]=r(e[t],e)})}t.exports=o})),J=s(((e,t)=>{var n=Bn(),r=Vn();function i(e,t,i){r(e,function(r,a){if(n(e,a))return t.call(i,e[a],a,e)})}t.exports=i})),qn=s(((e,t)=>{function n(e){return e}t.exports=n})),Jn=s(((e,t)=>{function n(e){return function(t){return t[e]}}t.exports=n})),Yn=s(((e,t)=>{var n=/^\[object (.*)\]$/,r=Object.prototype.toString,i;function a(e){return e===null?`Null`:e===i?`Undefined`:n.exec(r.call(e))[1]}t.exports=a})),Xn=s(((e,t)=>{var n=Yn();function r(e,t){return n(e)===t}t.exports=r})),Zn=s(((e,t)=>{var n=Xn();t.exports=Array.isArray||function(e){return n(e,`Array`)}})),Qn=s(((e,t)=>{var n=J(),r=Zn();function i(e,t){for(var n=-1,r=e.length;++n{var n=qn(),r=Jn(),i=Qn();function a(e,t){if(e==null)return n;switch(typeof e){case`function`:return t===void 0?e:function(n,r,i){return e.call(t,n,r,i)};case`object`:return function(t){return i(t,e)};case`string`:case`number`:return r(e)}}t.exports=a})),$n=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!1;return n(e,function(n,r){if(t(n,r,e))return a=!0,!1}),a}t.exports=i})),er=s(((e,t)=>{var n=$n();function r(e,t){return n(e,function(e){return e===t})}t.exports=r})),tr=s(((e,t)=>{function n(e){return!!e&&typeof e==`object`&&e.constructor===Object}t.exports=n})),nr=s(((e,t)=>{var n=J(),r=tr();function i(e,t){for(var a=0,o=arguments.length,s;++a{var n=J(),r=tr();function i(e,t){for(var r=0,i=arguments.length,o;++r{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a=!0;return n(e,function(n,r){if(!t(n,r,e))return a=!1,!1}),a}t.exports=i})),ar=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Object`)}t.exports=r})),or=s(((e,t)=>{function n(e,t){return e===t?e!==0||1/e==1/t:e!==e&&t!==t}t.exports=n})),sr=s(((e,t)=>{var n=Bn(),r=ir(),i=ar(),a=or();function o(e){return function(t,r){return n(this,r)&&e(t,this[r])}}function s(e,t){return n(this,t)}function c(e,t,n){return n||=a,!i(e)||!i(t)?n(e,t):r(e,o(n),t)&&r(t,s,e)}t.exports=c})),cr=s(((e,t)=>{var n=Gn(),r=Un(),i=J();function a(e,t){return n(r(arguments,1),function(t){i(t,function(t,n){e[n]??(e[n]=t)})}),e}t.exports=a})),lr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){t(e,n,r)&&(a[n]=e)}),a}t.exports=i})),ur=s(((e,t)=>{var n=$n(),r=Y();function i(e,t,i){t=r(t,i);var a;return n(e,function(e,n,r){if(t(e,n,r))return a=e,!0}),a}t.exports=i})),dr=s(((e,t)=>{var n=J(),r=tr();function i(e,t,a,o){return n(e,function(e,n){var s=a?a+`.`+n:n;o!==0&&r(e)?i(e,t,s,o-1):t[s]=e}),t}function a(e,t){return e==null?{}:(t??=-1,i(e,{},``,t))}t.exports=a})),fr=s(((e,t)=>{function n(e){switch(typeof e){case`string`:case`number`:case`boolean`:return!0}return e==null}t.exports=n})),pr=s(((e,t)=>{fr();function n(e,t){for(var n=t.split(`.`),r=n.pop();t=n.shift();)if(e=e[t],e==null)return;return e[r]}t.exports=n})),mr=s(((e,t)=>{var n=pr(),r;function i(e,t){return n(e,t)!==r}t.exports=i})),hr=s(((e,t)=>{var n=J();t.exports=Object.keys||function(e){var t=[];return n(e,function(e,n){t.push(n)}),t}})),gr=s(((e,t)=>{var n=J(),r=Y();function i(e,t,i){t=r(t,i);var a={};return n(e,function(e,n,r){a[n]=t(e,n,r)}),a}t.exports=i})),_r=s(((e,t)=>{var n=J();function r(e,t){var r=!0;return n(t,function(t,n){if(e[n]!==t)return r=!1}),r}t.exports=r})),vr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return 1/0;if(e.length&&!t)return Math.max.apply(Math,e);t=n(t,r);for(var i,a=-1/0,o,s,c=-1,l=e.length;++ca&&(a=s,i=o);return i}t.exports=r})),yr=s(((e,t)=>{var n=J();function r(e){var t=[];return n(e,function(e,n){t.push(e)}),t}t.exports=r})),br=s(((e,t)=>{var n=vr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),xr=s(((e,t)=>{var n=J();function r(e,t){for(var r=0,a=arguments.length,o;++r{var n=Yn(),r=tr(),i=xr();function a(e){switch(n(e)){case`Object`:return o(e);case`Array`:return l(e);case`RegExp`:return s(e);case`Date`:return c(e);default:return e}}function o(e){return r(e)?i({},e):e}function s(e){var t=``;return t+=e.multiline?`m`:``,t+=e.global?`g`:``,t+=e.ignoreCase?`i`:``,new RegExp(e.source,t)}function c(e){return new Date(+e)}function l(e){return e.slice()}t.exports=a})),Cr=s(((e,t)=>{var n=Sr(),r=J(),i=Yn(),a=tr();function o(e,t){switch(i(e)){case`Object`:return s(e,t);case`Array`:return c(e,t);default:return n(e)}}function s(e,t){if(a(e)){var n={};return r(e,function(e,n){this[n]=o(e,t)},n),n}else if(t)return t(e);else return e}function c(e,t){for(var n=[],r=-1,i=e.length;++r{var n=Bn(),r=Cr(),i=ar();function a(){for(var e=1,t,o,s,c=r(arguments[0]);s=arguments[e++];)for(t in s)n(s,t)&&(o=s[t],i(o)&&i(c[t])?c[t]=a(c[t],o):c[t]=r(o));return c}t.exports=a})),Tr=s(((e,t)=>{var n=Y();function r(e,t,r){if(e==null||!e.length)return-1/0;if(e.length&&!t)return Math.min.apply(Math,e);t=n(t,r);for(var i,a=1/0,o,s,c=-1,l=e.length;++c{var n=Tr(),r=yr();function i(e,t){return n(r(e),t)}t.exports=i})),Dr=s(((e,t)=>{var n=Gn();function r(e,t){return t&&n(t.split(`.`),function(t){e[t]||(e[t]={}),e=e[t]}),e}t.exports=r})),Or=s(((e,t)=>{function n(e,t,n){if(n||=0,e==null)return-1;for(var r=e.length,i=n<0?r+n:n;i{var n=Or();function r(e,t){return n(e,t)!==-1}t.exports=r})),Ar=s(((e,t)=>{var n=Un(),r=kr();function i(e,t){var i=typeof arguments[1]==`string`?n(arguments,1):arguments[1],a={};for(var o in e)e.hasOwnProperty(o)&&!r(i,o)&&(a[o]=e[o]);return a}t.exports=i})),jr=s(((e,t)=>{var n=Un();function r(e,t){for(var r=typeof arguments[1]==`string`?n(arguments,1):arguments[1],i={},a=0,o;o=r[a++];)i[o]=e[o];return i}t.exports=r})),Mr=s(((e,t)=>{var n=gr(),r=Jn();function i(e,t){return n(e,r(t))}t.exports=i})),Nr=s(((e,t)=>{var n=J();function r(e){var t=0;return n(e,function(){t++}),t}t.exports=r})),Pr=s(((e,t)=>{var n=J(),r=Nr();function i(e,t,i,a){var o=arguments.length>2;if(!r(e)&&!o)throw Error(`reduce of empty object with no initial value`);return n(e,function(e,n,r){o?i=t.call(a,i,e,n,r):(i=e,o=!0)}),i}t.exports=i})),Fr=s(((e,t)=>{var n=lr(),r=Y();function i(e,t,i){return t=r(t,i),n(e,function(e,n,r){return!t(e,n,r)},i)}t.exports=i})),Ir=s(((e,t)=>{var n=Xn();function r(e){return n(e,`Function`)}t.exports=r})),Lr=s(((e,t)=>{var n=Ir();function r(e,t){var r=e[t];if(r!==void 0)return n(r)?r.call(e):r}t.exports=r})),Rr=s(((e,t)=>{var n=Dr();function r(e,t,r){var i=/^(.+)\.(.+)$/.exec(t);i?n(e,i[1])[i[2]]=r:e[t]=r}t.exports=r})),zr=s(((e,t)=>{var n=mr();function r(e,t){if(n(e,t)){for(var r=t.split(`.`),i=r.pop();t=r.shift();)e=e[t];return delete e[i]}else return!0}t.exports=r})),Br=s(((e,t)=>{t.exports={bindAll:Kn(),contains:er(),deepFillIn:nr(),deepMatches:Qn(),deepMixIn:rr(),equals:sr(),every:ir(),fillIn:cr(),filter:lr(),find:ur(),flatten:dr(),forIn:Vn(),forOwn:J(),functions:Hn(),get:pr(),has:mr(),hasOwn:Bn(),keys:hr(),map:gr(),matches:_r(),max:br(),merge:wr(),min:Er(),mixIn:xr(),namespace:Dr(),omit:Ar(),pick:jr(),pluck:Mr(),reduce:Pr(),reject:Fr(),result:Lr(),set:Rr(),size:Nr(),some:$n(),unset:zr(),values:yr()}})),Vr=s(((e,t)=>{Object.defineProperty(e,"__esModule",{value:!0}),e.default=(0,Br().map)({A:{x:.44758,y:.40745},C:{x:.31006,y:.31616},D50:{x:.34567,y:.35851},D65:{x:.31272,y:.32903},D55:{x:.33243,y:.34744},D75:{x:.29903,y:.31488}},function(e){return[100*(e.x/e.y),100,100*(1-e.x-e.y)/e.y]}),t.exports=e.default})),Hr=s(((e,t)=>{Object.defineProperty(e,"__esModule",{value:!0});var n=Math,r=n.pow,i=n.sign,a=n.abs,o={decode:function(e){return e<=.04045?e/12.92:r((e+.055)/1.055,2.4)},encode:function(e){return e<=.0031308?12.92*e:1.055*r(e,1/2.4)-.055}},s={encode:function(e){return e<.001953125?16*e:r(e,1/1.8)},decode:function(e){return e<16*.001953125?e/16:r(e,1.8)}};function c(e){return{decode:function(t){return i(t)*r(a(t),e)},encode:function(t){return i(t)*r(a(t),1/e)}}}e.default={sRGB:{r:{x:.64,y:.33},g:{x:.3,y:.6},b:{x:.15,y:.06},gamma:o},"Adobe RGB":{r:{x:.64,y:.33},g:{x:.21,y:.71},b:{x:.15,y:.06},gamma:c(2.2)},"Wide Gamut RGB":{r:{x:.7347,y:.2653},g:{x:.1152,y:.8264},b:{x:.1566,y:.0177},gamma:c(563/256)},"ProPhoto RGB":{r:{x:.7347,y:.2653},g:{x:.1596,y:.8404},b:{x:.0366,y:1e-4},gamma:s}},t.exports=e.default})),Ur=s((e=>{Object.defineProperty(e,"__esModule",{value:!0});function t(e){return[[e[0][0],e[1][0],e[2][0]],[e[0][1],e[1][1],e[2][1]],[e[0][2],e[1][2],e[2][2]]]}function n(e){return e[0][0]*(e[2][2]*e[1][1]-e[2][1]*e[1][2])+e[1][0]*(e[2][1]*e[0][2]-e[2][2]*e[0][1])+e[2][0]*(e[1][2]*e[0][1]-e[1][1]*e[0][2])}function r(e){var t=1/n(e);return[[(e[2][2]*e[1][1]-e[2][1]*e[1][2])*t,(e[2][1]*e[0][2]-e[2][2]*e[0][1])*t,(e[1][2]*e[0][1]-e[1][1]*e[0][2])*t],[(e[2][0]*e[1][2]-e[2][2]*e[1][0])*t,(e[2][2]*e[0][0]-e[2][0]*e[0][2])*t,(e[1][0]*e[0][2]-e[1][2]*e[0][0])*t],[(e[2][1]*e[1][0]-e[2][0]*e[1][1])*t,(e[2][0]*e[0][1]-e[2][1]*e[0][0])*t,(e[1][1]*e[0][0]-e[1][0]*e[0][1])*t]]}function i(e,t){return[e[0][0]*t[0]+e[0][1]*t[1]+e[0][2]*t[2],e[1][0]*t[0]+e[1][1]*t[1]+e[1][2]*t[2],e[2][0]*t[0]+e[2][1]*t[1]+e[2][2]*t[2]]}function a(e,t){return[[e[0][0]*t[0],e[0][1]*t[1],e[0][2]*t[2]],[e[1][0]*t[0],e[1][1]*t[1],e[1][2]*t[2]],[e[2][0]*t[0],e[2][1]*t[1],e[2][2]*t[2]]]}function o(e,t){return[[e[0][0]*t[0][0]+e[0][1]*t[1][0]+e[0][2]*t[2][0],e[0][0]*t[0][1]+e[0][1]*t[1][1]+e[0][2]*t[2][1],e[0][0]*t[0][2]+e[0][1]*t[1][2]+e[0][2]*t[2][2]],[e[1][0]*t[0][0]+e[1][1]*t[1][0]+e[1][2]*t[2][0],e[1][0]*t[0][1]+e[1][1]*t[1][1]+e[1][2]*t[2][1],e[1][0]*t[0][2]+e[1][1]*t[1][2]+e[1][2]*t[2][2]],[e[2][0]*t[0][0]+e[2][1]*t[1][0]+e[2][2]*t[2][0],e[2][0]*t[0][1]+e[2][1]*t[1][1]+e[2][2]*t[2][1],e[2][0]*t[0][2]+e[2][1]*t[1][2]+e[2][2]*t[2][2]]]}e.transpose=t,e.determinant=n,e.inverse=r,e.multiply=i,e.scalar=a,e.product=o})),Wr=s((e=>{Object.defineProperty(e,"__esModule",{value:!0});var t=Math.PI;function n(e){for(var n=e*180/t;n<0;)n+=360;for(;n>360;)n-=360;return n}function r(e){for(var n=t*e/180;n<0;)n+=2*t;for(;n>2*t;)n-=2*t;return n}e.fromRadian=n,e.toRadian=r})),Gr=s((e=>{Object.defineProperty(e,"__esModule",{value:!0});var t=Math.round;function n(e){return e[0]==`#`&&(e=e.slice(1)),e.length<6&&(e=e.split(``).map(function(e){return e+e}).join(``)),e.match(/../g).map(function(e){return parseInt(e,16)/255})}function r(e){return`#`+e.map(function(e){return e=t(255*e).toString(16),e.length<2&&(e=`0`+e),e}).join(``)}e.fromHex=n,e.toHex=r})),Kr=s(((e,t)=>{Object.defineProperty(e,"__esModule",{value:!0});var n=o(Ur()),r=a(Vr()),i=a(Hr());function a(e){return e&&e.__esModule?e:{default:e}}function o(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function s(){var e=arguments.length<=0||arguments[0]===void 0?i.default.sRGB:arguments[0],t=arguments.length<=1||arguments[1]===void 0?r.default.D65:arguments[1],a=[e.r,e.g,e.b],o=n.transpose(a.map(function(e){return[e.x/e.y,1,(1-e.x-e.y)/e.y]})),s=e.gamma,c=n.multiply(n.inverse(o),t),l=n.scalar(o,c),u=n.inverse(l);return{fromRgb:function(e){return n.multiply(l,e.map(s.decode))},toRgb:function(e){return n.multiply(u,e).map(s.encode)}}}e.default=s,t.exports=e.default})),qr=s(((e,t)=>{t.exports={illuminant:Vr(),workspace:Hr(),matrix:Ur(),degree:Wr(),rgb:Gr(),xyz:Kr()}})),Jr=s((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.cfs=e.distance=e.lerp=e.corLerp=void 0;var t=Br();function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);ti/2&&(e>t?t+=i:e+=i),((1-n)*e+n*t)%(i||1/0)}function u(e,t,n){var r={};for(var i in e)r[i]=l(e[i],t[i],n,i);return r}function d(e,t){var n=0;for(var r in e)n+=o(e[r]-t[r],2);return s(n)}function f(e){return t.merge.apply(void 0,r(e.split(``).map(function(e){return n({},e,!0)})))}e.corLerp=l,e.lerp=u,e.distance=d,e.cfs=f})),Yr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,"__esModule",{value:!0});var r=qr(),i=Jr();function a(e,t){var a=arguments.length<=2||arguments[2]===void 0?1e-6:arguments[2],o=-a,s=1+a,c=Math,l=c.min,u=c.max,d=n([`000`,`fff`].map(function(n){return t.fromXyz(e.fromRgb(r.rgb.fromHex(n)))}),2),f=d[0],p=d[1];function m(n){var r=e.toRgb(t.toXyz(n));return[r.map(function(e){return e>=o&&e<=s}).reduce(function(e,t){return e&&t},!0),r]}function h(e,t){for(var r=arguments.length<=2||arguments[2]===void 0?.001:arguments[2];(0,i.distance)(e,t)>r;){var a=(0,i.lerp)(e,t,.5);n(m(a),1)[0]?e=a:t=a}return e}function g(e){return(0,i.lerp)(f,p,e)}function _(e){return e.map(function(e){return u(o,l(s,e))})}return{contains:m,limit:h,spine:g,crop:_}}e.default=a,t.exports=e.default})),Xr=s((e=>{var t=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,"__esModule",{value:!0}),e.toNotation=e.fromNotation=e.toHue=e.fromHue=void 0;var n=Jr(),r=Math.floor,i=[{s:`R`,h:20.14,e:.8,H:0},{s:`Y`,h:90,e:.7,H:100},{s:`G`,h:164.25,e:1,H:200},{s:`B`,h:237.53,e:1.2,H:300},{s:`R`,h:380.14,e:.8,H:400}],a=i.map(function(e){return e.s}).slice(0,-1).join(``);function o(e){e50){var o=[n,t];t=o[0],n=o[1],i=100-i}return i<1?a[t]:a[t]+i.toFixed()+a[n]}e.fromHue=o,e.toHue=s,e.fromNotation=l,e.toNotation=u})),Zr=s(((e,t)=>{var n=function(){function e(e,t){var n=[],r=!0,i=!1,a=void 0;try{for(var o=e[Symbol.iterator](),s;!(r=(s=o.next()).done)&&(n.push(s.value),!(t&&n.length===t));r=!0);}catch(e){i=!0,a=e}finally{try{!r&&o.return&&o.return()}finally{if(i)throw a}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw TypeError(`Invalid attempt to destructure non-iterable instance`)}}();Object.defineProperty(e,"__esModule",{value:!0});var r=qr(),i=s(Xr()),a=Jr(),o=Br();function s(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var c=Math,l=c.pow,u=c.sqrt,d=c.exp,f=c.abs,p=c.sign,m=Math,h=m.sin,g=m.cos,_=m.atan2,v={average:{F:1,c:.69,N_c:1},dim:{F:.9,c:.59,N_c:.9},dark:{F:.8,c:.535,N_c:.8}},y=[[.7328,.4296,-.1624],[-.7036,1.6975,.0061],[.003,.0136,.9834]],b=[[.38971,.68898,-.07868],[-.22981,1.1834,.04641],[0,0,1]],x=y,S=r.matrix.inverse(y),C=r.matrix.product(b,r.matrix.inverse(y)),w=r.matrix.product(y,r.matrix.inverse(b)),T={whitePoint:r.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ee=(0,a.cfs)(`QJMCshH`),E=(0,a.cfs)(`JCh`);function D(){var e=arguments.length<=0||arguments[0]===void 0?{}:arguments[0],t=arguments.length<=1||arguments[1]===void 0?ee:arguments[1];e=(0,o.merge)(T,e);var a=e.whitePoint,s=e.adaptingLuminance,c=e.backgroundLuminance,m=v[e.surroundType],b=m.F,D=m.c,O=m.N_c,te=a[1],k=1/(5*s+1),A=.2*l(k,4)*5*s+.1*l(1-l(k,4),2)*l(5*s,1/3),ne=c/te,re=.725*l(1/ne,.2),ie=re,ae=1.48+u(ne),oe=e.discounting?1:b*(1-1/3.6*d(-(s+42)/92)),j=n(r.matrix.multiply(y,a).map(function(e){return oe*te/e+1-oe}),3),se=j[0],M=j[1],ce=j[2],N=pe(de(le(a)));function le(e){var t=n(r.matrix.multiply(x,e),3),i=t[0],a=t[1],o=t[2];return[se*i,M*a,ce*o]}function ue(e){var t=n(e,3),i=t[0],a=t[1],o=t[2];return r.matrix.multiply(S,[i/se,a/M,o/ce])}function de(e){return r.matrix.multiply(C,e).map(function(e){var t=l(A*f(e)/100,.42);return p(e)*400*t/(27.13+t)+.1})}function fe(e){return r.matrix.multiply(w,e.map(function(e){var t=e-.1;return p(t)*100/A*l(27.13*f(t)/(400-f(t)),1/.42)}))}function pe(e){var t=n(e,3),r=t[0],i=t[1],a=t[2];return(r*2+i+a/20-.305)*re}function me(e){return 4/D*u(e/100)*(N+4)*l(A,.25)}function he(e){return 6.25*l(D*e/((N+4)*l(A,.25)),2)}function ge(e){return e*l(A,.25)}function _e(e,t){return l(e/100,2)*t/l(A,.25)}function ve(e){return e/l(A,.25)}function ye(e,t){return 100*u(e/t)}function be(e,t){var n=t.Q,r=t.J,a=t.M,o=t.C,s=t.s,c=t.h,l=t.H,u={};return e.J&&(u.J=isNaN(r)?he(n):r),e.C&&(isNaN(o)?isNaN(a)?(n=isNaN(n)?me(r):n,u.C=_e(s,n)):u.C=ve(a):u.C=t.C),e.h&&(u.h=isNaN(c)?i.toHue(l):c),e.Q&&(u.Q=isNaN(n)?me(r):n),e.M&&(u.M=isNaN(a)?ge(o):a),e.s&&(isNaN(s)?(n=isNaN(n)?me(r):n,a=isNaN(a)?ge(o):a,u.s=ye(a,n)):u.s=s),e.H&&(u.H=isNaN(l)?i.fromHue(c):l),u}function P(e){var i=de(le(e)),a=n(i,3),o=a[0],s=a[1],c=a[2],d=o-s*12/11+c/11,f=(o+s-2*c)/9,p=_(f,d),m=r.degree.fromRadian(p),h=1/4*(g(p+2)+3.8),v=100*l(pe(i)/N,D*ae);return be(t,{J:v,C:l(5e4/13*O*ie*h*u(d*d+f*f)/(o+s+21/20*c),.9)*u(v/100)*l(1.64-l(.29,ne),.73),h:m})}function F(e){var t=be(E,e),n=t.J,i=t.C,a=t.h,o=r.degree.toRadian(a),s=l(i/(u(n/100)*l(1.64-l(.29,ne),.73)),10/9),c=1/4*(g(o+2)+3.8),d=N*l(n/100,1/D/ae),p=5e4/13*O*ie*c/s,m=d/re+.305,_=m*61/20*460/1403,v=61/20*220/1403,y=21/20*6300/1403-27/1403,b=h(o),x=g(o),S,C;return s===0||isNaN(s)?S=C=0:f(b)>=f(x)?(C=_/(p/b+v*x/b+y),S=C*x/b):(S=_/(p/x+v+y*b/x),C=S*b/x),ue(fe([20/61*m+451/1403*S+288/1403*C,20/61*m-891/1403*S-261/1403*C,20/61*m-220/1403*S-6300/1403*C]))}return{fromXyz:P,toXyz:F,fillOut:be}}e.default=D,t.exports=e.default})),Qr=s(((e,t)=>{Object.defineProperty(e,"__esModule",{value:!0});var n=qr(),r=Math,i=r.sqrt,a=r.pow,o=r.exp,s=r.log,c=r.cos,l=r.sin,u=r.atan2,d={LCD:{K_L:.77,c_1:.007,c_2:.0053},SCD:{K_L:1.24,c_1:.007,c_2:.0363},UCS:{K_L:1,c_1:.007,c_2:.0228}};function f(){var e=d[arguments.length<=0||arguments[0]===void 0?`UCS`:arguments[0]],t=e.K_L,r=e.c_1,f=e.c_2;function p(e){var t=e.J,i=e.M,a=e.h,o=n.degree.toRadian(a),u=(1+100*r)*t/(1+r*t),d=1/f*s(1+f*i);return{J_p:u,a_p:d*c(o),b_p:d*l(o)}}function m(e){var t=e.J_p,s=e.a_p,c=e.b_p,l=-t/(r*t-100*r-1),d=(o(f*i(a(s,2)+a(c,2)))-1)/f,p=u(c,s);return{J:l,M:d,h:n.degree.fromRadian(p)}}function h(e,n){return i(a((e.J_p-n.J_p)/t,2)+a(e.a_p-n.a_p,2)+a(e.b_p-n.b_p,2))}return{fromCam:p,toCam:m,distance:h}}e.default=f,t.exports=e.default})),$r=s(((e,t)=>{var n=Jr(),r=Yr(),i=Zr(),a=Qr(),o=Xr();t.exports={gamut:r,cfs:n.cfs,lerp:n.lerp,cam:i,ucs:a,hq:o}})),ei=l(qr(),1),ti=l($r(),1);function ni(e){let t=new q;return t.rgb_r=e[0],t.rgb_g=e[1],t.rgb_b=e[2],t.rgbToHsluv(),[t.hsluv_h,t.hsluv_s,t.hsluv_l]}function ri(e){let t=new q;return t.hsluv_h=e[0],t.hsluv_s=e[1],t.hsluv_l=e[2],t.hsluvToRgb(),[t.rgb_r,t.rgb_g,t.rgb_b]}var ii=ti.default.cam({whitePoint:ei.default.illuminant.D65,adaptingLuminance:40,backgroundLuminance:20,surroundType:`average`,discounting:!1},ti.default.cfs(`JCh`)),ai=ei.default.xyz(ei.default.workspace.sRGB,ei.default.illuminant.D65),oi=e=>ai.toRgb(ii.toXyz({J:e[0],C:e[1],h:e[2]})),si=e=>{let t=ii.fromXyz(ai.fromRgb(e));return[t.J,t.C,t.h]},[ci,li]=(()=>{let e={k_l:1,c1:.007,c2:.0228},t=Math.PI,n=64/t/5,r=1/(5*n+1),i=.2*r**4*(5*n)+.1*(1-r**4)**2*(5*n)**(1/3);return[n=>{let[r,a,o]=n,s=a*i**.25,c=(1+100*e.c1)*r/(1+e.c1*r);c/=e.k_l;let l=1/e.c2*Math.log(1+e.c2*s),u=l*Math.cos(t/180*o),d=l*Math.sin(t/180*o);return[c,u,d]},n=>{let[r,a,o]=n,s=Math.sqrt(a*a+o*o),c=(Math.exp(s*e.c2)-1)/e.c2,l=(180/t*Math.atan2(o,a)+360)%360,u=c/i**.25;return[r/(1+e.c1*(100-r)),u,l]}]})(),ui=e=>oi(li(e)),di=e=>ci(si(e)),fi=console;fi.color=(e,t=``)=>{let n=K(e).luminance();fi.log(`%c${e} ${t}`,`background-color: ${e};padding: 5px; border-radius: 5px; color: ${n>.5?`#000`:`#fff`}`)},fi.ramp=(e,t=1)=>{fi.log(`%c `,`font-size: 1px;line-height: 16px;background: ${K.getCSSGradient(e,t)};padding: 0 0 0 200px; border-radius: 2px;`)};var pi=(e,t,n,r,i,a,o=.1)=>{if(e===n||t===r)return!0;let s=(r-t)/(n-e),c=(a+i/s-t+s*e)/(s+1/s),l=a+i/s-c/s;return(i-c)**2+(a-l)**2{let i=(t[0]+n[0])/2,a=e(i);return pi(...t,...n,i,a,r)?null:[i,a]},hi=(e,t,n,r=.1)=>{let i=(n-t)/10,a=[];for(let r=t;rMath.round(e*10**t)/10**t,_i=(e,t=1,n=90,r=.005)=>{let i=hi(t=>e(t).gl()[0],0,t,r),a=hi(t=>e(t).gl()[1],0,t,r),o=hi(t=>e(t).gl()[2],0,t,r);return`linear-gradient(${n}deg, ${Array.from(new Set([...i.map(e=>gi(e[0])),...a.map(e=>gi(e[0])),...o.map(e=>gi(e[0]))].sort((e,t)=>e-t))).map(t=>`${e(t).hex()} ${gi(t*100)}%`).join()});`},vi=e=>{e.Color.prototype.jch=function(){return si(this._rgb.slice(0,3).map(e=>e/255))},e.jch=(...t)=>new e.Color(...oi(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.jab=function(){return di(this._rgb.slice(0,3).map(e=>e/255))},e.jab=(...t)=>new e.Color(...ui(t).map(e=>Math.floor(e*255)),`rgb`),e.Color.prototype.hsluv=function(){return ni(this._rgb.slice(0,3).map(e=>e/255))},e.hsluv=(...t)=>new e.Color(...ri(t).map(e=>Math.floor(e*255)),`rgb`);let t=e.interpolate,n={jch:si,jab:di,hsluv:ni},r=(e,t,n)=>(Math.abs(e-t)>360/2&&(e>t?t+=360:e+=360),((1-n)*e+n*t)%360);e.interpolate=(i,a,o=.5,s=`lrgb`)=>{if(n[s]){typeof i!=`object`&&(i=new e.Color(i)),typeof a!=`object`&&(a=new e.Color(a));let t=n[s](i.gl()),c=n[s](a.gl()),l=Number.isNaN(i.hsl()[0]),u=Number.isNaN(a.hsl()[0]),d,f,p;switch(s){case`hsluv`:t[1]<1e-10&&(t[0]=c[0]),t[1]===0&&(t[1]=c[1]),c[1]<1e-10&&(c[0]=t[0]),c[1]===0&&(c[1]=t[1]),d=r(t[0],c[0],o),f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o;break;case`jch`:l&&(t[2]=c[2]),u&&(c[2]=t[2]),d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=r(t[2],c[2],o);break;default:d=t[0]+(c[0]-t[0])*o,f=t[1]+(c[1]-t[1])*o,p=t[2]+(c[2]-t[2])*o}return e[s](d,f,p).alpha(i.alpha()+o*(a.alpha()-i.alpha()))}return t(i,a,o,s)},e.getCSSGradient=_i},X={mainTRC:2.4,get mainTRCencode(){return 1/this.mainTRC},sRco:.2126729,sGco:.7151522,sBco:.072175,normBG:.56,normTXT:.57,revTXT:.62,revBG:.65,blkThrs:.022,blkClmp:1.414,scaleBoW:1.14,scaleWoB:1.14,loBoWoffset:.027,loWoBoffset:.027,deltaYmin:5e-4,loClip:.1,mFactor:1.9468554433171,get mFactInv(){return 1/this.mFactor},mOffsetIn:.0387393816571401,mExpAdj:.283343396420869,get mExp(){return this.mExpAdj/this.blkClmp},mOffsetOut:.312865795870758};function yi(e,t,n=-1){let r=[0,1.1];if(isNaN(e)||isNaN(t)||Math.min(e,t)r[1])return 0;let i=0,a=0,o=`BoW`;return e=e>X.blkThrs?e:e+(X.blkThrs-e)**+X.blkClmp,t=t>X.blkThrs?t:t+(X.blkThrs-t)**+X.blkClmp,Math.abs(t-e)e?(i=(t**+X.normBG-e**+X.normTXT)*X.scaleBoW,a=i-X.loClip?0:i+X.loWoBoffset),n<0?a*100:n==0?Math.round(Math.abs(a)*100)+``+o+``:Number.isInteger(n)?(a*100).toFixed(n):0)}function bi(e=[0,0,0]){function t(e){return(e/255)**X.mainTRC}return X.sRco*t(e[0])+X.sGco*t(e[1])+X.sBco*t(e[2])}var xi=(e,t,n,r,i,a,o,s,c)=>{let l=1-c,u=l*l,d=u*l,f=c*c*c;return{x:d*e+u*3*c*n+l*3*c*c*i+f*o,y:d*t+u*3*c*r+l*3*c*c*a+f*s}},Si=(e,t)=>{let n=[],r={x:+e[0],y:+e[1]};for(let i=0,a=e.length;a-2*!t>i;i+=2){let o=[{x:+e[i-2],y:+e[i-1]},{x:+e[i],y:+e[i+1]},{x:+e[i+2],y:+e[i+3]},{x:+e[i+4],y:+e[i+5]}];t?i?a-4===i?o[3]={x:+e[0],y:+e[1]}:a-2===i&&(o[2]={x:+e[0],y:+e[1]},o[3]={x:+e[2],y:+e[3]}):o[0]={x:+e[a-2],y:+e[a-1]}:a-4===i?o[3]=o[2]:i||(o[0]={x:+e[i],y:+e[i+1]}),n.push([r.x,r.y,(-o[0].x+6*o[1].x+o[2].x)/6,(-o[0].y+6*o[1].y+o[2].y)/6,(o[1].x+6*o[2].x-o[3].x)/6,(o[1].y+6*o[2].y-o[3].y)/6,o[2].x,o[2].y]),r=o[2]}return n},Ci=(e,t,n,r,i,a,o,s)=>{let c=e,l=t,u=0;for(let d=1;d<5;d++){let{x:f,y:p}=xi(e,t,n,r,i,a,o,s,d/5);u+=Math.hypot(f-c,p-l),c=f,l=p}return u+=Math.hypot(o-c,s-l),u},wi=(e,t,n,r,i,a,o,s)=>{let c=Math.floor(Ci(e,t,n,r,i,a,o,s)*.75),l=[],u=0;for(let d=0;d<=c;d++){let f=xi(e,t,n,r,i,a,o,s,d/c),p=Math.round(f.x);if(l[p]=f.y,p-u>1){let e=l[u],t=l[p];for(let n=u+1;nl[Math.round(e)]||null},Ti={CAM02:`jab`,CAM02p:`jch`,HEX:`hex`,HSL:`hsl`,HSLuv:`hsluv`,HSV:`hsv`,LAB:`lab`,LCH:`lch`,RGB:`rgb`,OKLAB:`oklab`,OKLCH:`oklch`};function Z(e,t=0){let n=10**t;return Math.round(e*n)/n}function Ei(e,t){let n;return n=e>1?(e-1)*t+1:e<-1?(e+1)*t-1:1,Z(n,2)}function Di(e){return K(String(e)).jch()}function Oi(e){return K(String(e)).hsluv()}function ki(e,t,n){let r=[[],[],[]];if(e.forEach((e,n)=>r.forEach((r,i)=>r.push(t[n],e[i]))),n===`hcl`){let e=r[1];for(let t=1;t{let t=[];for(let n=1;n{e[t]=e[n]}),t.length=0;break}if(t.length){let n=K(`#ccc`).jch()[2];t.forEach(t=>{e[t]=n})}t.length=0;for(let n=e.length-1;n>0;n-=2)if(Number.isNaN(e[n]))t.push(n);else{t.forEach(t=>{e[t]=e[n]});break}for(let t=1;tSi(e).map(e=>wi(...e)));return e=>{let t=i.map(t=>{for(let n=0;nr*t**e+i}function ji({swatches:e,colorKeys:t,colorspace:n,colorSpace:r=n??`LAB`,shift:i=1,fullScale:a=!0,smooth:o=!1,distributeLightness:s=`linear`,sortColor:c=!0,asFun:l=!1}={}){n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead.");let u=Ti[r];if(!u)throw Error(`Colorspace “${r}” not supported`);if(!t)throw Error(`Colorkeys missing: returned “${t}”`);let d;if(a)d=t.map(t=>e-e*(K(t).jch()[0]/100)).sort((e,t)=>e-t).concat(e),d.unshift(0);else{let n=t.map(e=>K(e).jch()[0]/100),r=Math.min(...n),i=Math.max(...n);d=n.map(t=>t===0||isNaN((t-r)/(i-r))?0:e-(t-r)/(i-r)*e).sort((e,t)=>e-t)}let f=Ai(i,[1,e],[1,e]);if(f=d.map(e=>Math.max(0,f(e))),d=f,s===`polynomial`){let t=e=>Math.sqrt(Math.sqrt((e**2.25+e**4)/2));d=f.map(t=>t/e).map(n=>t(n)*e)}let p=t.map((e,t)=>({colorKeys:Di(e),index:t})).sort((e,t)=>t.colorKeys[0]-e.colorKeys[0]).map(e=>t[e.index]),m=[],h;if(a){let e=u===`lch`?K.lch(...K(`#fff`).lch()):`#ffffff`,t=u===`lch`?K.lch(...K(`#000`).lch()):`#000000`;m=[e,...p,t]}else m=c?p:t;let g;if(o){let t=m;if(m=m.map(e=>K(String(e))[u]()),u===`hcl`&&m.forEach(e=>{e[1]=Number.isNaN(e[1])?0:e[1]}),u===`jch`)for(let e=0;eh(t))}else h=K.scale(m.map(e=>typeof e==`object`&&e.constructor===K.Color?e:String(e))).domain(d).mode(u);return l?h:(!o||o===!1?h.colors(e):g).filter(e=>e!=null)}function Mi(e,t){let n=[],r={};return Object.keys(e).forEach(n=>{r[e[n][t]]=e[n]}),Object.keys(r).forEach(e=>n.push(r[e])),n}function Ni(e){return Number.isNaN(e)?0:e}function Pi(e,t,n=!1){if(!e)throw Error(`Cannot convert color value of “${e}”`);if(!Ti[t])throw Error(`Cannot convert to colorspace “${t}”`);let r=Ti[t],i=K(String(e))[r]();if(t===`HSL`&&i.pop(),t===`HEX`){if(n){let t=K(String(e)).rgb();return{r:t[0],g:t[1],b:t[2]}}return i}let a={},o=i.map(Ni);o=o.map((e,t)=>{let i=Z(e),o=t;r===`hsluv`&&(o+=2);let s=r.charAt(o);return r===`jch`&&s===`c`&&(s=`C`),a[s===`j`?`J`:s]=i,r in{lab:1,lch:1,jab:1,jch:1}?n||(s===`l`||s===`j`)&&(i+=`%`):r!==`hsluv`&&(s===`s`||s===`l`||s===`v`)&&(a[s]=Z(e,2),n||(i=Z(e*100),i+=`%`)),i});let s=`${r}(${o.join(`, `)})`;return n?a:s}function Fi(e,t,n){let r=[e,t,n].map(e=>(e/=255,e<=.03928?e/12.92:((e+.055)/1.055)**2.4));return r[0]*.2126+r[1]*.7152+r[2]*.0722}function Ii(e,t,n,r=`wcag2`){if(n===void 0){let e=K.rgb(...t).hsluv()[2];n=Z(e/100,2)}if(r===`wcag2`){let r=Fi(e[0],e[1],e[2]),i=Fi(t[0],t[1],t[2]),a=(r+.05)/(i+.05),o=(i+.05)/(r+.05);return n<.5?a>=1?a:-o:a<1?o:a===1?a:-a}else if(r===`wcag3`)return n<.5?yi(bi(e),bi(t))*-1:yi(bi(e),bi(t));else throw Error(`Contrast calculation method ${r} unsupported; use 'wcag2' or 'wcag3'`)}function Li(e,t){if(!e)throw Error(`Array undefined`);if(!Array.isArray(e))throw Error(`Passed object is not an array`);let n=t===`wcag2`?0:1;return Math.min(...e.filter(e=>e>=n))}function Ri(e,t){if(!e)throw Error(`Ratios undefined`);e=e.sort((e,t)=>e-t);let n=Li(e,t),r=e.indexOf(n),i=[],a=e.slice(0,r),o=e.slice(r,e.length);for(let e=0;ee-t),i}var zi=(e,t,n,r,i)=>{let a=3e3,o=ji({swatches:a,colorKeys:e._modifiedKeys,colorspace:e._colorspace,shift:1,smooth:e._smooth,asFun:!0}),s={},c=e=>{if(s[e])return s[e];let r=Ii(K(o(e)).rgb(),t,n,i);return s[e]=r,r},l=e=>{let t=c(0).01&&o;)o--,n/=2,iu.push(o(l(+e)))),u},Q=class{constructor({name:e,colorKeys:t,colorspace:n,colorSpace:r=n??`RGB`,ratios:i,smooth:a=!1,output:o=`HEX`,saturation:s=100}){if(n!==void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),this._name=e,this._colorKeys=t,this._modifiedKeys=t,this._colorspace=r,this._ratios=i,this._smooth=a,this._output=o,this._saturation=s,!this._name)throw Error(`Color missing name`);if(!this._colorKeys)throw Error(`Color Keys are undefined`);if(!Ti[this._colorspace])throw Error(`Colorspace “${r}” not supported`);if(!Ti[this._output])throw Error(`Output “${this._output}” not supported`);for(let e=0;e{let n=K(`${t}`).oklch(),r=n[1]*(this._saturation/100),i=K.oklch(n[0],r,n[2]),a=K.rgb(i).hex();e.push(a)}),this._modifiedKeys=e,this._generateColorScale()}_generateColorScale(){this._colorScale=ji({swatches:3e3,colorKeys:this._modifiedKeys,colorSpace:this._colorspace,shift:1,smooth:this._smooth,asFun:!0})}},Bi=class extends Q{get backgroundColorScale(){return this._backgroundColorScale||this._generateColorScale(),this._backgroundColorScale}_generateColorScale(){Q.prototype._generateColorScale.call(this);let e=ji({swatches:1e3,colorKeys:this._colorKeys,colorspace:this._colorspace,shift:1,smooth:this._smooth});e.push(...this.colorKeys);let t=Mi(e.map((e,t)=>({value:Math.round(Oi(e)[2]),index:t})),`value`).map(t=>e[t.index]);return t.length>=101&&(t.length=100,t.push(`#ffffff`)),this._backgroundColorScale=t.map(e=>Pi(e,this._output)),this._backgroundColorScale}},Vi=class{constructor({colors:e,backgroundColor:t,lightness:n,contrast:r=1,saturation:i=100,output:a=`HEX`,formula:o=`wcag2`}){if(this._output=a,this._colors=e,this._lightness=n,this._saturation=i,this._formula=o,this._setBackgroundColor(t),this._setBackgroundColorValue(),this._contrast=r,!this._colors)throw Error(`No colors are defined`);if(!this._backgroundColor)throw Error(`Background color is undefined`);if(e.forEach(e=>{if(!e.ratios)throw Error(`Color ${e.name}'s ratios are undefined`)}),!Ti[this._output])throw Error(`Output “${a}” not supported`);this._saturation<100&&this._updateColorSaturation(this._saturation),this._findContrastColors(),this._findContrastColorPairs(),this._findContrastColorValues()}set formula(e){this._formula=e,this._findContrastColors()}get formula(){return this._formula}set contrast(e){this._contrast=e,this._findContrastColors()}get contrast(){return this._contrast}set lightness(e){this._lightness=e,this._setBackgroundColor(this._backgroundColor),this._findContrastColors()}get lightness(){return this._lightness}set saturation(e){this._saturation=e,this._updateColorSaturation(e),this._findContrastColors()}get saturation(){return this._saturation}set backgroundColor(e){this._setBackgroundColor(e),this._findContrastColors()}get backgroundColorValue(){return this._backgroundColorValue}get backgroundColor(){return this._backgroundColor}set colors(e){this._colors=e,this._findContrastColors()}get colors(){return this._colors}set addColor(e){this._colors.push(e),this._findContrastColors()}set removeColor(e){let t=this._colors.filter(t=>t.name!==e.name);this._colors=t,this._findContrastColors()}set updateColor(e){if(Array.isArray(e))for(let t=0;tn.name===e[t].color);n=n[0];let r=this._colors.indexOf(n),i=this._colors.filter(n=>n.name!==e[t].color);e[t].name&&(n.name=e[t].name),e[t].colorKeys&&(n.colorKeys=e[t].colorKeys),e[t].ratios&&(n.ratios=e[t].ratios),(e[t].colorSpace!==void 0||e[t].colorspace!==void 0)&&(e[t].colorspace!==void 0&&e[t].colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),n.colorSpace=e[t].colorSpace??e[t].colorspace),e[t].smooth&&(n.smooth=e[t].smooth),n._generateColorScale(),i.splice(r,0,n),this._colors=i}else{let t=this._colors.filter(t=>t.name===e.color);t=t[0];let n=this._colors.indexOf(t),r=this._colors.filter(t=>t.name!==e.color);e.name&&(t.name=e.name),e.colorKeys&&(t.colorKeys=e.colorKeys),e.ratios&&(t.ratios=e.ratios),(e.colorSpace!==void 0||e.colorspace!==void 0)&&(e.colorspace!==void 0&&e.colorSpace===void 0&&console.warn("Leonardo: `colorspace` is deprecated. Use `colorSpace` instead."),t.colorSpace=e.colorSpace??e.colorspace),e.smooth&&(t.smooth=e.smooth),t._generateColorScale(),r.splice(n,0,t),this._colors=r}this._findContrastColors()}set output(e){this._output=e,this._colors.forEach(e=>{e.output=this._output}),this._backgroundColor.output=this._output,this._findContrastColors()}get output(){return this._output}get contrastColors(){return this._contrastColors}get contrastColorPairs(){return this._contrastColorPairs}get contrastColorValues(){return this._contrastColorValues}_setBackgroundColor(e){if(typeof e==`string`){let t=new Bi({name:`background`,colorKeys:[e],output:`RGB`}),n=Z(K(String(e)).hsluv()[2]);this._backgroundColor=t,this._lightness=n,this._backgroundColorValue=t[this._lightness]}else{e.output=`RGB`;let t=e.backgroundColorScale[this._lightness];this._backgroundColor=e,this._backgroundColorValue=t}}_setBackgroundColorValue(){this._backgroundColorValue=this._backgroundColor.backgroundColorScale[this._lightness]}_updateColorSaturation(e){this._colors.map(t=>{t.saturation=e})}_findContrastColors(){let e=K(String(this._backgroundColorValue)).rgb(),t=this._lightness/100,n={background:Pi(this._backgroundColorValue,this._output)},r=[],i=[],a={...n};return r.push(n),this._colors.map(n=>{if(n.ratios!==void 0){let o,s=[],c={name:n.name,values:s},l;Array.isArray(n.ratios)?l=n.ratios:Array.isArray(n.ratios)||(o=Object.keys(n.ratios),l=Object.values(n.ratios)),l=l.map(e=>Ei(+e,this._contrast));let u=zi(n,e,t,l,this._formula).map(e=>Pi(e,this._output));for(let e=0;e{let t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return[Number.parseInt(t[1],16),Number.parseInt(t[2],16),Number.parseInt(t[3],16)]},Wi=(e,t,n)=>{let r=e/255,i=t/255,a=n/255,o=Math.min(r,i,a),s=Math.max(r,i,a),c=s-o,l=0,u=0,d=0;return l=c===0?0:s===r?(i-a)/c%6:s===i?(a-r)/c+2:(r-i)/c+4,l=Math.round(l*60),l<0&&(l+=360),d=(s+o)/2,u=c===0?0:c/(1-Math.abs(2*d-1)),u=+(u*100).toFixed(1),d=+(d*100).toFixed(1),[l,u,Math.round(d)]},Gi=(e,t,n,r)=>{let i=n/100,a=t*Math.min(i,1-i)/100,o=t=>{let n=(t+e/30)%12,r=i-a*Math.max(Math.min(n-3,9-n,1),-1);return Math.round(255*r).toString(16).padStart(2,`0`).toUpperCase()},s=o(0),c=o(8),l=o(4),u=((e,t,n)=>Math.min(Math.max(e,t),n))(r,0,1);return`#${s}${c}${l}${Math.round(u*255).toString(16).padStart(2,`0`).toUpperCase()}`},Ki=(e,t,n=1)=>{let r=Ui(e),i=Ui(t===`white`?`#FFFFFF`:t===`black`?`#000000`:t),a=r.map((e,t)=>[(e-i[t])/(255-i[t]),(e-i[t])/(0-i[t])]),o=Hi(Math.max(...a.flat().filter(e=>/^-?\d+\.?\d*$/.test(e)))),s=r.map((e,t)=>Math.round((e-i[t]+i[t]*o)/o));if(s.includes(NaN)){let e=Wi(r[0],r[1],r[2]);return{h:e[0],s:Math.round(e[1]*n),l:e[2],a:1}}let c=Wi(s[0],s[1],s[2]);return{h:c[0],s:Math.round(c[1]*n),l:c[2],a:o}},qi={backgroundColor:`gray`,colorSpace:`OKLCH`,colorSmoothing:!1,formula:`wcag2`,output:`HEX`,colors:{gray:[$(215,20,90),$(215,8,50),$(215,6,25)],red:[$(358,100,58),$(350,100,30)],orange:[$(32,100,48),$(12,100,30)],yellow:[$(50,100,50),$(25,100,20)],lime:[$(100,68,50),$(115,86,25)],green:[$(163,87,42),$(168,100,25)],cyan:[$(185,80,45),$(200,98,35)],blue:[$(212,98,46),$(222,95,25)],purple:[$(258,94,64),$(265,100,35)],fuchsia:[$(295,56,50),$(285,80,25)],pink:[$(334,90,50),$(330,91,25)]},themes:{light:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16.75],contrast:1,lightness:100,saturation:100},dark:{ratios:[1.03,1.06,1.12,1.25,1.5,1.75,2.25,3.5,5.25,6.5,8,10.5,13.75,16],contrast:1,lightness:6,saturation:97},lightHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:100,saturation:100},darkHc:{ratios:[1.06,1.12,1.25,1.37,1.75,2.25,3.25,4.75,8.87,10,11.75,13.25,16,17],contrast:1,lightness:6,saturation:97}}};function $(e,t,n){return K.hsl(e,t/100,n/100).hex()}function Ji(e,t){let n=e.colorSpace,r=e.colorSmoothing,i=e.themes[t].ratios,a=new Bi({name:`gray`,colorKeys:e.colors.gray,colorspace:n,ratios:i,smooth:r}),o=new Q({name:`blue`,colorKeys:e.colors.blue,colorspace:n,ratios:i,smooth:r}),s=new Q({name:`cyan`,colorKeys:e.colors.cyan,colorspace:n,ratios:i,smooth:r}),c=new Q({name:`fuchsia`,colorKeys:e.colors.fuchsia,colorspace:n,ratios:i,smooth:r}),l=new Q({name:`green`,colorKeys:e.colors.green,colorspace:n,ratios:i,smooth:r}),u=new Q({name:`lime`,colorKeys:e.colors.lime,colorspace:n,ratios:i,smooth:r}),d=new Q({name:`orange`,colorKeys:e.colors.orange,colorspace:n,ratios:i,smooth:r}),f=new Q({name:`pink`,colorKeys:e.colors.pink,colorspace:n,ratios:i,smooth:r}),p=new Q({name:`purple`,colorKeys:e.colors.purple,colorspace:n,ratios:i,smooth:r}),m={gray:a,red:new Q({name:`red`,colorKeys:e.colors.red,colorspace:n,ratios:i,smooth:r}),orange:d,yellow:new Q({name:`yellow`,colorKeys:e.colors.yellow,colorspace:n,ratios:i,smooth:r}),lime:u,green:l,cyan:s,blue:o,purple:p,fuchsia:c,pink:f};return e.colors.custom&&(m.custom=new Q({name:`custom`,colorKeys:e.colors.custom,colorspace:n,ratios:i,smooth:r})),new Vi({colors:Object.values(m),backgroundColor:m[e.backgroundColor],contrast:e.themes[t].contrast,lightness:e.themes[t].lightness,saturation:e.themes[t].saturation,output:e.output,formula:e.formula}).contrastColors}function Yi(e){let t={};for(let n of Object.keys(e.themes))t[n]=Ji(e,n);return t}function Xi(e){qi.colors.custom=[e];let t=Yi(qi);return Object.fromEntries(Object.entries(t).map(([e,t])=>{let n=t.find(e=>e&&e.name===`custom`),r=Object.fromEntries(n.values.map(({name:e,value:t})=>[e,t]));for(let[e,n]of Object.entries(r)){let i=Ki(n,t[0].background);r[`alpha${e.charAt(0).toUpperCase()+e.slice(1)}`]=Gi(i.h,i.s,i.l,i.a)}return[e,r]}))}return e.generateCustomColors=Xi,e.generateThemesJson=Yi,e.hslToHex=$,e.leonardoConfig=qi,e})({}); \ No newline at end of file diff --git a/libraries/compound/src/main/res/drawable/ic_compound_crop.xml b/libraries/compound/src/main/res/drawable/ic_compound_crop.xml index dc9513a239..c82f5cb686 100644 --- a/libraries/compound/src/main/res/drawable/ic_compound_crop.xml +++ b/libraries/compound/src/main/res/drawable/ic_compound_crop.xml @@ -4,9 +4,10 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M6,4a1,1 0,0 0,-1 1v2H3a1,1 0,1 0,0 2h2v8a3,3 0,0 0,3 3h8v2a1,1 0,0 0,2 0v-2h2a1,1 0,1 0,0 -2h-2v-8a3,3 0,0 0,-3 -3H7V5a1,1 0,0 0,-1 -1m9,5a1,1 0,0 1,1 1v8H8a1,1 0,0 1,-1 -1V9z" + android:fillColor="#FF000000" + android:fillType="evenOdd"/> From 144e3c6b5d76cc8f6d6c0214f9af2bf33de90f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 16:13:50 +0200 Subject: [PATCH 344/471] Adding fastlane file for version 26.06.1 --- fastlane/metadata/android/en-US/changelogs/202606010.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202606010.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202606010.txt b/fastlane/metadata/android/en-US/changelogs/202606010.txt new file mode 100644 index 0000000000..08c06cf192 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202606010.txt @@ -0,0 +1,2 @@ +Main changes in this version: fixed an issue with devices not supporting Vulkan, added image editing before sending and an option for custom notification sounds, improved start times, several other bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 25264dd3e3b303f4b29c1e9da8b69d293197f1bd Mon Sep 17 00:00:00 2001 From: S1m <31284753+p1gp1g@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:19:52 +0200 Subject: [PATCH 345/471] Fallback to the default push gateway on error (#5742) * Add more HTTP response code returning NoMatrixGateway Fix Push notifications with Mozilla's autopush that returns 406 * Update gateway resolver tests to match new known errors * Use default gateway on error if no gateway is recorded yet * Update gateway resolver tests to return default gateway on error --- .../unifiedpush/UnifiedPushGatewayUrlResolver.kt | 5 +++-- .../unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt index 78a569a211..1155293567 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt @@ -29,8 +29,9 @@ class DefaultUnifiedPushGatewayUrlResolver( ): String { return when (gatewayResult) { is UnifiedPushGatewayResolverResult.Error -> { - // Use previous gateway if any, or the provided one - unifiedPushStore.getPushGateway(instance) ?: gatewayResult.gateway + // Use previous gateway if any, or the default one + unifiedPushStore.getPushGateway(instance) + ?: defaultPushGatewayHttpUrlProvider.provide() } UnifiedPushGatewayResolverResult.ErrorInvalidUrl, UnifiedPushGatewayResolverResult.NoMatrixGateway -> { diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt index 3f437bdafa..664ff26d15 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt @@ -60,7 +60,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest { } @Test - fun `resolve Error returns the url if no current url is available`() { + fun `resolve Error returns the default gateway url if no current url is available`() { val sut = createDefaultUnifiedPushGatewayUrlResolver( unifiedPushStore = FakeUnifiedPushStore( getPushGatewayResult = { instance -> @@ -73,7 +73,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest { gatewayResult = UnifiedPushGatewayResolverResult.Error("aUrl"), instance = "instance", ) - assertThat(result).isEqualTo("aUrl") + assertThat(result).isEqualTo(FakeDefaultPushGatewayHttpUrlProvider().provide()) } private fun createDefaultUnifiedPushGatewayUrlResolver( From 9eed7de7992c1431a3402cdef54820c233d6639d Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 4 Jun 2026 14:33:07 +0000 Subject: [PATCH 346/471] Update screenshots --- libraries/compound/screenshots/Compound Icons - Dark.png | 4 ++-- libraries/compound/screenshots/Compound Icons - Light.png | 4 ++-- libraries/compound/screenshots/Compound Icons - Rtl.png | 4 ++-- .../compound/screenshots/Compound Vector Icons - Dark.png | 4 ++-- .../compound/screenshots/Compound Vector Icons - Light.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_0_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_1_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_3_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_5_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_7_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_8_en.png | 4 ++-- ...s.impl.attachments.preview_AttachmentsPreviewView_9_en.png | 4 ++-- ...raries.designsystem.theme.components_AllIcons_Icons_en.png | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/compound/screenshots/Compound Icons - Dark.png b/libraries/compound/screenshots/Compound Icons - Dark.png index 1209b824be..6d5057eaac 100644 --- a/libraries/compound/screenshots/Compound Icons - Dark.png +++ b/libraries/compound/screenshots/Compound Icons - Dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7511b9256afc6d001b53576959e69549683ab4cb81b49031aeda027d14178aa -size 245703 +oid sha256:78386a58e91be3213485f60909ec8d83cd535aabcec775aadf7b36e77d8542f0 +size 245853 diff --git a/libraries/compound/screenshots/Compound Icons - Light.png b/libraries/compound/screenshots/Compound Icons - Light.png index 58ba1591ed..f4f66317c3 100644 --- a/libraries/compound/screenshots/Compound Icons - Light.png +++ b/libraries/compound/screenshots/Compound Icons - Light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83a12b138543be1e17da8aa562bd95d1de18c0093da50d9a14196f905584bf7c -size 258948 +oid sha256:477ab18ffa1ca81ca5e4c9f30f4466b19e14fcaddf641fc860c6be19a0e33d4f +size 259185 diff --git a/libraries/compound/screenshots/Compound Icons - Rtl.png b/libraries/compound/screenshots/Compound Icons - Rtl.png index 06daf04971..d0374a65fb 100644 --- a/libraries/compound/screenshots/Compound Icons - Rtl.png +++ b/libraries/compound/screenshots/Compound Icons - Rtl.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73b37a7cd34f2ffe285011f67c2b38ed5399cbea40b3115c6c5115536f2902f2 -size 260171 +oid sha256:7edeebf9876aecd262fceb7d55a4bfd30a859a69b708af8dd4538ffc0f9be419 +size 260400 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Dark.png b/libraries/compound/screenshots/Compound Vector Icons - Dark.png index 5c95b36a89..0d8e07d9fa 100644 --- a/libraries/compound/screenshots/Compound Vector Icons - Dark.png +++ b/libraries/compound/screenshots/Compound Vector Icons - Dark.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49a9372bea521e4ee948bbd4f7616310c411ee46f84ffd562d9b35bc9bfeb4d9 -size 97239 +oid sha256:cc6cc2d2e89c7460217c2075046e41bef727407ee422dcdea23f161f48441904 +size 97346 diff --git a/libraries/compound/screenshots/Compound Vector Icons - Light.png b/libraries/compound/screenshots/Compound Vector Icons - Light.png index 38764691f4..74b4c859c7 100644 --- a/libraries/compound/screenshots/Compound Vector Icons - Light.png +++ b/libraries/compound/screenshots/Compound Vector Icons - Light.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:add37e4d84f03a33caf1079eaa99631682f5ef6418096629f96433520e519b20 -size 103742 +oid sha256:4df16aee4a3a8b10c33e9724904c72b8dc502725204d2f86cf62cc381b35f4a1 +size 103870 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png index ccca24ef6a..91c617002c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a14113653096095323ecb94cb3dd694985717b8585c8cfc6b7a45cf7a2483a79 -size 402427 +oid sha256:14ad2df78cdd8422279c413e9ed2f709636d09cf04b3ee4ada5b6b53e8e42c8f +size 402328 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png index c511e0e162..74b24f7194 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95c64e1e7055dd048b88f0e19a57fe696ee93505d74f96bc4f1915fcce769d7d -size 402072 +oid sha256:45ade3f4baaf0b91300a6829fbe42818d754ccca64b1b090195ee40eee744ee9 +size 401973 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png index ccca24ef6a..91c617002c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a14113653096095323ecb94cb3dd694985717b8585c8cfc6b7a45cf7a2483a79 -size 402427 +oid sha256:14ad2df78cdd8422279c413e9ed2f709636d09cf04b3ee4ada5b6b53e8e42c8f +size 402328 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png index f8fbf8c1a1..171ada1369 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccedfe061af8a77da7ddf6b20d23899bfff986fd6c74b87480b588e148ddd8de -size 89013 +oid sha256:c6e4dd5eae99a0454d6918b65baa3ca36c2266abfd9fb37dda961268e55827a9 +size 88994 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png index 5f90adbd8a..c21cab9cb2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1feeb32f9dce486c54db41727d9d9801f606353008945988331c2ce391dec451 -size 75364 +oid sha256:58ba9fd5edd2aa366b62a0a8880b6a3f6f7142861180c4109ba4904c43101433 +size 75343 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png index d7cdc540f7..42cf04b201 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bfda29fcaade9508e0d742b0de7ac230d41a358e98755e4b08f1ed7d5affe30 -size 408106 +oid sha256:43a670de0512321ca3e1b5bf0c967c6bd8504df542781c4973c8806b8b95abe9 +size 408029 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png index f06abb6c7b..84cc698a63 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fdc4e2d82297927d2062586398fda3f3b7f6febfde8888a062229caf713ff54 -size 85154 +oid sha256:6c0ef41360eb82092680b5b262bf1ef8449636387124c5aef940a727b3bc6aee +size 85137 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png index 9c80a760b7..fa2208b50f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_AllIcons_Icons_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:787bcdf784abedc6858fdab6c7b5dac9e36d42a1047ac0bad181449dbb757585 -size 117936 +oid sha256:de697fcf50bd244978da1143a80f872650a424c3e308fd2c4c66005f30cbf6c5 +size 118141 From d54f10046d7dbf5e9251a1db0f016c63dd1b8c6f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 4 Jun 2026 15:20:23 +0200 Subject: [PATCH 347/471] Ensure the video preview can be played several times. Closes #6956 --- .../impl/local/video/MediaVideoView.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt index d89762b72e..21279a598e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt @@ -33,10 +33,13 @@ import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.media3.common.MediaItem +import androidx.media3.common.ParserException +import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Player.STATE_READY import androidx.media3.common.Timeline import androidx.media3.common.util.UnstableApi +import androidx.media3.exoplayer.ExoPlaybackException import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView @@ -158,6 +161,19 @@ private fun ExoPlayerMediaVideoView( isReady = playbackState == STATE_READY, ) } + + override fun onPlayerError(error: PlaybackException) { + super.onPlayerError(error) + Timber.w(error, "Player error") + if (error is ExoPlaybackException && error.cause is ParserException) { + // The cause can be: + // androidx.media3.common.ParserException: Invalid NAL length {contentIsMalformed=true, dataType=1} + // This has been observed when the user wants to play a second time a recorded video. + // Workaround the issue #6956, start the playback again + exoPlayer.prepare() + exoPlayer.play() + } + } } } From e7992bfd403615479ef1c9fb0a6c312f6cceb309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 16:13:28 +0200 Subject: [PATCH 348/471] Setting version for the release 26.06.1 --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 69d7791e6b..f87eed1a40 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -45,7 +45,7 @@ private const val versionMonth = 6 * Release number in the month. Value must be in [0,99]. * Do not update this value. it is updated by the release script. */ -private const val versionReleaseNumber = 0 +private const val versionReleaseNumber = 1 object Versions { /** From fb7ad271c91b6288dad9f0f59dd664b35658f311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 16:13:50 +0200 Subject: [PATCH 349/471] Adding fastlane file for version 26.06.1 --- fastlane/metadata/android/en-US/changelogs/202606010.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/202606010.txt diff --git a/fastlane/metadata/android/en-US/changelogs/202606010.txt b/fastlane/metadata/android/en-US/changelogs/202606010.txt new file mode 100644 index 0000000000..08c06cf192 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202606010.txt @@ -0,0 +1,2 @@ +Main changes in this version: fixed an issue with devices not supporting Vulkan, added image editing before sending and an option for custom notification sounds, improved start times, several other bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases \ No newline at end of file From 488e5d1e3a5327143748207735cccf6beadbbcff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 4 Jun 2026 16:55:52 +0200 Subject: [PATCH 350/471] Changelog for version 26.06.1 --- CHANGES.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9775c2fea5..f24c813de7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,92 @@ +Changes in Element X v26.06.1 +============================= + +The release of `v26.06.0` was cancelled because it contained some changes that would prevent some users with devices having no Vulkan support from being able to install the app. This release contains the same changes as `v26.06.0` but with the Vulkan requirement removed. + +## What's Changed +### ✨ Features +* Image edition before sending: crop and rotate. by @bmarty in https://github.com/element-hq/element-x-android/pull/6842 +* Use a raw key for the SDK stores for new sessions by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6835 +* Notification settings: pick custom sounds for messages and call ringtones by @bmarty in https://github.com/element-hq/element-x-android/pull/6897 +### 🙌 Improvements +* Do not show membership/profile events in public rooms by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6360 +* Use `runBlocking` for the token refresh logic by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6863 +* Move empty day separator filtering to a timeline post-processor by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6866 +* Add better logs to track token update failures by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6859 +* Read map tiler custom style from matrix .well-known file by @bmarty in https://github.com/element-hq/element-x-android/pull/6886 +* change(permissions) : allow to change ShareLiveLocation permission by @ganfra in https://github.com/element-hq/element-x-android/pull/6890 +* Avoid SQLCipher key derivation by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6774 +* change: replace the maplibre-compose UserLocationState to a simpler one by @ganfra in https://github.com/element-hq/element-x-android/pull/6913 +* Change : location provider by @ganfra in https://github.com/element-hq/element-x-android/pull/6935 +* Add flip actions to image edition by @bmarty in https://github.com/element-hq/element-x-android/pull/6949 +* Remove the `FloatingDateBadge` feature flag by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6950 +### 🐛 Bugfixes +* Fix media viewer flickering by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6800 +* Fix 'Conversation label cannot be empty' error by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6823 +* Release proximity wakelock on Element Call when call ends by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6825 +* Hide edit pencil from accessibility by @bmarty in https://github.com/element-hq/element-x-android/pull/6826 +* Don't compress images sent through the Files attachment picker by @cizra in https://github.com/element-hq/element-x-android/pull/6755 +* [a11y] Improve accessibility of video and audio player by @bmarty in https://github.com/element-hq/element-x-android/pull/6830 +* [a11y] Improve accessibility of screen headers. by @bmarty in https://github.com/element-hq/element-x-android/pull/6827 +* Add mark as read / unread in room details by @bmarty in https://github.com/element-hq/element-x-android/pull/6818 +* Fix formatting inconsistencies in latest event summaries by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6855 +* Fix public read receipts being sent by mistake by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6838 +* Fix app having a pink top bar in the recent app list when PIN lock is setup by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6903 +* Create log messages from `WebView` just once by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6923 +* Use secondary color icon for leading icons by @bmarty in https://github.com/element-hq/element-x-android/pull/6926 +* Use reverse ordering for `FilterEmptyDayPostProcessor` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6927 +* Make Vulkan a *not required* feature by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6961 +### 🗣 Translations +* Sync Strings - new translations to Catalan by @ElementBot in https://github.com/element-hq/element-x-android/pull/6856 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6921 +### 🧱 Build +* Remove DI-generated code from Kover reports by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6834 +* Disable cron jobs and private SSH key jobs in forks by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6821 +* Revert "Disable cron jobs and private SSH key jobs in forks" by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6905 +* Downgrade detekt compose rules by @bmarty in https://github.com/element-hq/element-x-android/pull/6846 +* Add Stefan's etiquette to the project. by @bmarty in https://github.com/element-hq/element-x-android/pull/6909 +### 🚧 In development 🚧 +* [Link new device] Rotate QrCode instead of showing an error by @bmarty in https://github.com/element-hq/element-x-android/pull/6817 +* Fix image resizing behavior. by @bmarty in https://github.com/element-hq/element-x-android/pull/6924 +### Dependency upgrades +* Update dependency net.zetetic:sqlcipher-android to v4.16.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6819 +* Update dependencyAnalysis to v3.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6813 +* Update metro to v1.1.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6832 +* Update dependency io.element.android:element-call-embedded to v0.19.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6833 +* Update kotlin to v2.3.8 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6822 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.20 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6831 +* Update media3 to v1.10.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6816 +* Update peaceiris/actions-gh-pages action to v4.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6820 +* Update dependencyAnalysis to v3.12.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6840 +* Update codecov/codecov-action action to v6.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6864 +* Update dependency io.element.android:element-call-embedded to v0.20.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6876 +* Update roborazzi to v1.62.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6858 +* Update dependency androidx.compose:compose-bom to v2026.05.01 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6888 +* Update roborazzi to v1.63.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6893 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.05.26 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6885 +* Update dependency org.maplibre.compose:maplibre-compose to v0.13.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6891 +* Update actions/stale action to v10.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6904 +* Update tspascoal/get-user-teams-membership action to v4.0.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6900 +* Update dependency org.maplibre.gl:android-sdk to v13.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6930 +* Update dependency org.unifiedpush.android:connector to v3.3.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6937 +* Update kotlin to v2.3.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6946 +* Update dependency com.posthog:posthog-android to v3.47.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6920 +* Import compound token from release 10.2.1 https://github.com/element-hq/compound-design-tokens/releases/tag/v10.2.1 by @bmarty in https://github.com/element-hq/element-x-android/pull/6942 +* Update dependency org.matrix.rustcomponents:sdk-android to v26.06.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6947 +* Update dependency com.github.matrix-org:matrix-analytics-events to v0.36.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6939 +### Others +* Make the avatar in the room member moderation bottom sheet clickable by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6705 +* Do not hide your own media for "Show media in timeline" by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6898 +* Report problem: add an optional TextField to provide a GitHub issue number. by @bmarty in https://github.com/element-hq/element-x-android/pull/6911 +* Fix read receipts not shown for calls by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6889 +* Reorder room detail items by @bmarty in https://github.com/element-hq/element-x-android/pull/6929 +* [Media viewer] File preview improvement by @bmarty in https://github.com/element-hq/element-x-android/pull/6933 +* [Media bottom sheet] UI iteration by @bmarty in https://github.com/element-hq/element-x-android/pull/6938 +* Add unread count to the room unread indicator by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6887 + + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.05.2...v26.06.1 + Changes in Element X v26.05.2 ============================= From 8722305e124249e6769fa37902f76feed55f5f37 Mon Sep 17 00:00:00 2001 From: Jenna Vassar Date: Thu, 4 Jun 2026 09:41:55 -0700 Subject: [PATCH 351/471] Update strings for ios parity --- .../impl/setup/SecureBackupSetupView.kt | 52 +++++++++---------- .../impl/src/main/res/values/temporary.xml | 52 +++++++++---------- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 2d086c8cf5..0f8bf7d043 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.autofill.ContentType import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.lerp import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentType @@ -149,8 +148,8 @@ private fun title(state: SecureBackupSetupState): String { // Custom flow has no "save your key" step — use per-step custom titles throughout. if (state.isCustomEntry()) { return when (state.customEntryStep) { - CustomEntryStep.Entry -> stringResource(id = R.string.screen_recovery_key_custom_title) - CustomEntryStep.Confirm -> stringResource(id = R.string.screen_recovery_key_custom_confirm_title) + CustomEntryStep.Entry -> stringResource(id = R.string.pro_screen_recovery_key_mode_input_title) + CustomEntryStep.Confirm -> stringResource(id = R.string.pro_screen_recovery_key_mode_confirm_title) } } return when (state.setupState) { @@ -171,8 +170,8 @@ private fun subtitle(state: SecureBackupSetupState): String? { if (!state.wellknownLoaded) return null if (state.isCustomEntry()) { return when (state.customEntryStep) { - CustomEntryStep.Entry -> stringResource(id = R.string.screen_recovery_key_custom_description) - CustomEntryStep.Confirm -> stringResource(id = R.string.screen_recovery_key_custom_confirm_description) + CustomEntryStep.Entry -> stringResource(id = R.string.pro_screen_recovery_key_mode_input_description) + CustomEntryStep.Confirm -> stringResource(id = R.string.pro_screen_recovery_key_mode_confirm_description) } } return when (state.setupState) { @@ -295,9 +294,8 @@ private fun CustomPassphraseEntry(state: SecureBackupSetupState) { } }, ), - supportingText = pluralStringResource( - id = R.plurals.screen_recovery_key_custom_requirement_length, - count = specs.minCharacterCount, + supportingText = stringResource( + id = R.string.pro_screen_recovery_key_mode_input_passphrase_field_footer, specs.minCharacterCount, ), ) @@ -317,7 +315,7 @@ private fun PassphraseStrengthIndicator( modifier: Modifier = Modifier, ) { val label = stringResource( - id = result?.strength?.labelRes() ?: R.string.screen_recovery_key_custom_strength_base, + id = result?.strength?.labelRes() ?: R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_base, ) val hint = result?.strength?.hintRes()?.let { stringResource(id = it) } val score = result?.score ?: 0f @@ -354,25 +352,25 @@ private fun PassphraseStrengthIndicator( } private fun CustomRecoveryPassphraseStrength.labelRes(): Int = when (this) { - CustomRecoveryPassphraseStrength.Garbage -> R.string.screen_recovery_key_custom_strength_garbage - CustomRecoveryPassphraseStrength.Weak -> R.string.screen_recovery_key_custom_strength_weak - CustomRecoveryPassphraseStrength.Moderate -> R.string.screen_recovery_key_custom_strength_moderate - CustomRecoveryPassphraseStrength.Okay -> R.string.screen_recovery_key_custom_strength_okay - CustomRecoveryPassphraseStrength.Strong -> R.string.screen_recovery_key_custom_strength_strong - CustomRecoveryPassphraseStrength.VeryStrong -> R.string.screen_recovery_key_custom_strength_very_strong - CustomRecoveryPassphraseStrength.UltraStrong -> R.string.screen_recovery_key_custom_strength_ultra_strong - CustomRecoveryPassphraseStrength.Mega -> R.string.screen_recovery_key_custom_strength_mega + CustomRecoveryPassphraseStrength.Garbage -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_garbage + CustomRecoveryPassphraseStrength.Weak -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_weak + CustomRecoveryPassphraseStrength.Moderate -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_moderate + CustomRecoveryPassphraseStrength.Okay -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_okay + CustomRecoveryPassphraseStrength.Strong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_strong + CustomRecoveryPassphraseStrength.VeryStrong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_very_strong + CustomRecoveryPassphraseStrength.UltraStrong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_ultra_strong + CustomRecoveryPassphraseStrength.Mega -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_mega } private fun CustomRecoveryPassphraseStrength.hintRes(): Int = when (this) { - CustomRecoveryPassphraseStrength.Garbage -> R.string.screen_recovery_key_custom_strength_hint_garbage - CustomRecoveryPassphraseStrength.Weak -> R.string.screen_recovery_key_custom_strength_hint_weak - CustomRecoveryPassphraseStrength.Moderate -> R.string.screen_recovery_key_custom_strength_hint_moderate - CustomRecoveryPassphraseStrength.Okay -> R.string.screen_recovery_key_custom_strength_hint_okay - CustomRecoveryPassphraseStrength.Strong -> R.string.screen_recovery_key_custom_strength_hint_strong - CustomRecoveryPassphraseStrength.VeryStrong -> R.string.screen_recovery_key_custom_strength_hint_very_strong - CustomRecoveryPassphraseStrength.UltraStrong -> R.string.screen_recovery_key_custom_strength_hint_ultra_strong - CustomRecoveryPassphraseStrength.Mega -> R.string.screen_recovery_key_custom_strength_hint_mega + CustomRecoveryPassphraseStrength.Garbage -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_garbage + CustomRecoveryPassphraseStrength.Weak -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_weak + CustomRecoveryPassphraseStrength.Moderate -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_moderate + CustomRecoveryPassphraseStrength.Okay -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_okay + CustomRecoveryPassphraseStrength.Strong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_strong + CustomRecoveryPassphraseStrength.VeryStrong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_very_strong + CustomRecoveryPassphraseStrength.UltraStrong -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_ultra_strong + CustomRecoveryPassphraseStrength.Mega -> R.string.pro_screen_recovery_key_mode_input_passphrase_strength_label_hint_mega } // Stops mirror iOS PasswordStrengthBar.gradient: Compound red900 / lime700 with the same vivid @@ -439,7 +437,7 @@ private fun CustomPassphraseConfirm(state: SecureBackupSetupState) { ), validity = if (state.customPassphraseMismatch) TextFieldValidity.Invalid else TextFieldValidity.None, supportingText = if (state.customPassphraseMismatch) { - stringResource(id = R.string.screen_recovery_key_custom_mismatch) + stringResource(id = R.string.pro_screen_recovery_key_mode_custom_mismatch) } else { null }, @@ -491,7 +489,7 @@ private fun ColumnScope.CustomButtons( } CustomEntryStep.Confirm -> { Button( - text = stringResource(id = R.string.screen_recovery_key_custom_finish_action), + text = stringResource(id = R.string.pro_screen_recovery_key_mode_confirm_finish_button), enabled = state.canSubmitCustomPassphrase, modifier = Modifier.fillMaxWidth(), onClick = { state.eventSink.invoke(SecureBackupSetupEvents.SubmitCustomPassphrase) }, diff --git a/features/securebackup/impl/src/main/res/values/temporary.xml b/features/securebackup/impl/src/main/res/values/temporary.xml index 3edbd84d73..c573a4c6e3 100644 --- a/features/securebackup/impl/src/main/res/values/temporary.xml +++ b/features/securebackup/impl/src/main/res/values/temporary.xml @@ -5,33 +5,31 @@ ~ Please see LICENSE files in the repository root for full details. --> - Enter a custom recovery key - Choose a recovery key that you can memorize. - Confirm your recovery key - Enter your recovery key again. - The recovery key you entered doesn\'t match - - Minimum %1$d character - Minimum %1$d characters - - Finish setup - Strength - Garbage - Weak - Moderate - Okay - Strong - Very strong - Super strong - Ideal - Cracking would take roughly milliseconds. - Cracking would take roughly minutes to months. - Cracking would take roughly years. - Cracking would take roughly hundreds of years. - Cracking would take roughly billions of years. - Cracking would take roughly trillions of years. - Cracking would take roughly quadrillions of years. - Cracking would take roughly quintillions of years. + Enter a recovery key + Choose a recovery key that you can memorize. + Confirm your recovery key + Enter your recovery key again. + Minimum %1$s characters + Finish setup + Strength + Garbage + Weak + Moderate + Okay + Strong + Very Strong + Super Strong + Ideal + Cracking would take roughly milliseconds. + Cracking would take roughly minutes to months. + Cracking would take roughly years. + Cracking would take roughly hundreds of years. + Cracking would take roughly billions of years. + Cracking would take roughly trillions of years. + Cracking would take roughly quadrillions of years. + Cracking would take roughly quintillions of years. + + The recovery key you entered doesn\'t match Loading recovery key requirements Passphrase strength: %1$s From 3b4a18a7908f495cc81f1675f25a6763acdae90a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:44:22 +0000 Subject: [PATCH 352/471] Update dependency com.google.firebase:firebase-bom to v34.14.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9657aaf15d..e9c0910ab4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -84,7 +84,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:34.13.0" +google_firebase_bom = "com.google.firebase:firebase-bom:34.14.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } From a10aa6cbe36da52fb05bfbbb2df19af2afd8dad1 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:26:46 +0000 Subject: [PATCH 353/471] Support thread root in drafts --- .../android/features/messages/impl/MessagesNode.kt | 2 +- .../impl/messagecomposer/MessageComposerPresenter.kt | 12 ++++++------ .../messages/impl/threads/ThreadedMessagesNode.kt | 2 +- .../MessageComposerPresenterSlashCommandTest.kt | 3 +++ .../messagecomposer/MessageComposerPresenterTest.kt | 2 ++ 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index a9ce2f5ba1..ef0e7100e2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -107,7 +107,7 @@ class MessagesNode( private val timelineController = TimelineController(room, room.liveTimeline) private val presenter = presenterFactory.create( navigator = this, - composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = false), + composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = false, threadRoot = null), timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this), actionListPresenter = actionListPresenterFactory.create( postProcessor = TimelineItemActionPostProcessor.Default, 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 e226318345..9e9fc15eec 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 @@ -49,6 +49,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -110,6 +111,7 @@ class MessageComposerPresenter( @Assisted private val navigator: MessagesNavigator, @Assisted private val timelineController: TimelineController, @Assisted private val isInThread: Boolean, + @Assisted private val threadRoot: ThreadId?, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val room: JoinedRoom, private val mediaPickerProvider: PickerProvider, @@ -139,6 +141,7 @@ class MessageComposerPresenter( timelineController: TimelineController, navigator: MessagesNavigator, isInThread: Boolean, + threadRoot: ThreadId?, ): MessageComposerPresenter } @@ -234,8 +237,7 @@ class MessageComposerPresenter( LaunchedEffect(Unit) { val draft = draftService.loadDraft( roomId = room.roomId, - // TODO support threads in composer - threadRoot = null, + threadRoot = threadRoot, isVolatile = false ) if (draft != null) { @@ -652,8 +654,7 @@ class MessageComposerPresenter( roomId = room.roomId, draft = draft, isVolatile = isVolatile, - // TODO support threads in composer - threadRoot = null, + threadRoot = threadRoot, ) } @@ -816,8 +817,7 @@ class MessageComposerPresenter( // Use the volatile draft only when coming from edit mode otherwise. val draft = draftService.loadDraft( roomId = room.roomId, - // TODO support threads in composer - threadRoot = null, + threadRoot = threadRoot, isVolatile = true ).takeIf { fromEdit } if (draft != null) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index be573fa92f..ade22eed41 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -112,7 +112,7 @@ class ThreadedMessagesNode( this.timelineController = timelineController return presenterFactory.create( navigator = this, - composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = true), + composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = true, threadRoot = inputs.threadRootEventId), timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this), // TODO add special processor for threaded timeline actionListPresenter = actionListPresenterFactory.create( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt index 116a1cfb5d..2a0ba9ff1b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper import io.element.android.features.messages.impl.utils.TextPillificationHelper import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.permalink.PermalinkParser @@ -272,11 +273,13 @@ class MessageComposerPresenterSlashCommandTest { draftService: ComposerDraftService = FakeComposerDraftService(), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), isInThread: Boolean = false, + threadRoot: ThreadId? = null, slashCommandService: SlashCommandService = FakeSlashCommandService(), ) = MessageComposerPresenter( navigator = navigator, sessionCoroutineScope = this, isInThread = isInThread, + threadRoot = threadRoot, room = room, mediaPickerProvider = pickerProvider, sessionPreferencesStore = sessionPreferencesStore, 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 e8d106a80f..ab9886a368 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 @@ -1535,11 +1535,13 @@ class MessageComposerPresenterTest { draftService: ComposerDraftService = FakeComposerDraftService(), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), isInThread: Boolean = false, + threadRoot: ThreadId? = null, slashCommandService: SlashCommandService = FakeSlashCommandService(), ) = MessageComposerPresenter( navigator = navigator, sessionCoroutineScope = this, isInThread = isInThread, + threadRoot = threadRoot, room = room, mediaPickerProvider = pickerProvider, sessionPreferencesStore = sessionPreferencesStore, From 165a8b776a5335551ebfe163b2a17cd81d508c6c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:04:41 +0200 Subject: [PATCH 354/471] Update dependency io.element.android:element-call-embedded to v0.20.1 (#6988) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 70b096873e..fbfb29110c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -239,7 +239,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.20.0" +element_call_embedded = "io.element.android:element-call-embedded:0.20.1" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } From 6219378ad3f8629d9937529997a3dd103ee9b4b9 Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:21:34 +0300 Subject: [PATCH 355/471] Fix "Sent" checkmark disappearing when the message is read and "Share presence" is disabled (#6985) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't compute read receipts if presence sharing is disabled * Update screenshots --------- Co-authored-by: ElementBot Co-authored-by: Jorge Martín --- .../list/PinnedMessagesListPresenter.kt | 3 +- .../pinned/list/PinnedMessagesListView.kt | 1 - .../impl/timeline/TimelinePresenter.kt | 13 ++++---- .../messages/impl/timeline/TimelineState.kt | 1 - .../impl/timeline/TimelineStateProvider.kt | 2 -- .../messages/impl/timeline/TimelineView.kt | 1 - .../components/ATimelineItemEventRow.kt | 2 -- .../components/TimelineItemCallNotifyView.kt | 15 ++++------ .../components/TimelineItemEventRow.kt | 2 -- .../TimelineItemEventRowWithRRPreview.kt | 3 -- .../TimelineItemGroupedEventsRow.kt | 9 +----- .../timeline/components/TimelineItemRow.kt | 5 ---- .../components/TimelineItemStateEventRow.kt | 3 -- .../receipt/TimelineItemReadReceiptView.kt | 30 ++++++++----------- .../factories/TimelineItemsFactory.kt | 12 +++++--- .../event/TimelineItemEventFactory.kt | 9 ++++-- .../messages/impl/MessagesViewTest.kt | 1 - 17 files changed, 43 insertions(+), 69 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index 9e5f5ba304..6e8974b871 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -208,7 +208,8 @@ class PinnedMessagesListPresenter( combine(timelineItemsFlow, room.membersStateFlow) { items, membersState -> timelineItemsFactory.replaceWith( timelineItems = items, - roomMembers = membersState.roomMembers().orEmpty() + roomMembers = membersState.roomMembers().orEmpty(), + renderReadReceipts = false, ) }.launchIn(this) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 9811b07c02..bfed13028b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -217,7 +217,6 @@ private fun PinnedMessagesListLoaded( timelineItem = timelineItem, timelineMode = Timeline.Mode.PinnedEvents, timelineRoomInfo = state.timelineRoomInfo, - renderReadReceipts = false, timelineProtectionState = state.timelineProtectionState, isLastOutgoingMessage = false, focusedEventId = null, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0941b5c3f1..6711a7fe25 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -138,9 +138,6 @@ class TimelinePresenter( val messageShieldDialogData: MutableState = remember { mutableStateOf(null) } val resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailurePresenter.present() - val renderReadReceipts by remember { - sessionPreferencesStore.isRenderReadReceiptsEnabled() - }.collectAsState(initial = true) val isLive by remember { timelineController.isLive() }.collectAsState(initial = true) @@ -252,13 +249,18 @@ class TimelinePresenter( } .launchIn(this) - combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState -> + combine( + timelineController.timelineItems(), + room.membersStateFlow, + sessionPreferencesStore.isRenderReadReceiptsEnabled(), + ) { items, membersState, renderReadReceipts -> val parent = analyticsService.getLongRunningTransaction(DisplayFirstTimelineItems) val transaction = parent?.startChild("timelineItemsFactory.replaceWith", "Processing timeline items") transaction?.putExtraData(AnalyticsUserData.TIMELINE_ITEM_COUNT, items.count().toString()) timelineItemsFactory.replaceWith( timelineItems = items, - roomMembers = membersState.roomMembers().orEmpty() + roomMembers = membersState.roomMembers().orEmpty(), + renderReadReceipts = renderReadReceipts, ) transaction?.finish() items @@ -313,7 +315,6 @@ class TimelinePresenter( timelineItems = timelineItems, timelineMode = timelineMode, timelineRoomInfo = timelineRoomInfo, - renderReadReceipts = renderReadReceipts, newEventState = newEventState.value, isLive = isLive, focusRequestState = focusRequestState.value, 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 03f0083856..d2f0faaa24 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 @@ -26,7 +26,6 @@ data class TimelineState( val timelineItems: ImmutableList, val timelineRoomInfo: TimelineRoomInfo, val timelineMode: Timeline.Mode, - val renderReadReceipts: Boolean, val newEventState: NewEventState, val isLive: Boolean, val focusRequestState: FocusRequestState, 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 3ed50f34a0..bd0b093ed5 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 @@ -51,7 +51,6 @@ import kotlin.random.Random fun aTimelineState( timelineItems: ImmutableList = persistentListOf(), timelineMode: Timeline.Mode = Timeline.Mode.Live, - renderReadReceipts: Boolean = false, timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(), focusedEventIndex: Int = -1, isLive: Boolean = true, @@ -70,7 +69,6 @@ fun aTimelineState( timelineItems = timelineItems, timelineMode = timelineMode, timelineRoomInfo = timelineRoomInfo, - renderReadReceipts = renderReadReceipts, newEventState = NewEventState.None, isLive = isLive, focusRequestState = focusRequestState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 0791366105..0a306aea1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -171,7 +171,6 @@ fun TimelineView( timelineMode = state.timelineMode, timelineRoomInfo = state.timelineRoomInfo, timelineProtectionState = timelineProtectionState, - renderReadReceipts = state.renderReadReceipts, isLastOutgoingMessage = state.isLastOutgoingMessage(timelineItem.identifier()), focusedEventId = state.focusedEventId, displayThreadSummaries = state.displayThreadSummaries, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt index 6be00fc14a..45f4a86a53 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt @@ -22,7 +22,6 @@ internal fun ATimelineItemEventRow( event: TimelineItem.Event, timelineMode: Timeline.Mode = Timeline.Mode.Live, timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(), - renderReadReceipts: Boolean = false, isLastOutgoingMessage: Boolean = false, timelineProtectionState: TimelineProtectionState = aTimelineProtectionState(), displayThreadSummaries: Boolean = false, @@ -30,7 +29,6 @@ internal fun ATimelineItemEventRow( event = event, timelineMode = timelineMode, timelineRoomInfo = timelineRoomInfo, - renderReadReceipts = renderReadReceipts, timelineProtectionState = timelineProtectionState, isLastOutgoingMessage = isLastOutgoingMessage, displayThreadSummaries = displayThreadSummaries, 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 fea4f92de5..a7ab44db54 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 @@ -52,7 +52,6 @@ internal fun TimelineItemCallNotifyView( timelineRoomInfo: TimelineRoomInfo, event: TimelineItem.Event, content: TimelineItemRtcNotificationContent, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, onLongClick: (TimelineItem.Event) -> Unit, onReadReceiptsClick: (TimelineItem.Event) -> Unit, @@ -106,7 +105,6 @@ internal fun TimelineItemCallNotifyView( isLastOutgoingMessage = isLastOutgoingMessage, receipts = event.readReceiptState.receipts, ), - renderReadReceipts = renderReadReceipts, onReadReceiptsClick = { onReadReceiptsClick(event) }, modifier = Modifier.padding(top = 4.dp), ) @@ -146,8 +144,10 @@ private fun getIcon( @PreviewsDayNight @Composable internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { - val readReceiptState = aTimelineItemReadReceipts( - receipts = List(3) { aReadReceiptData(it) }, + val readReceiptState = mutableListOf( + aTimelineItemReadReceipts( + receipts = List(3) { aReadReceiptData(it) }, + ) ) Column(modifier = Modifier.padding(bottom = 16.dp)) { listOf(false, true).forEach { isDm -> @@ -162,13 +162,10 @@ internal fun TimelineItemCallNotifyViewPreview() = ElementPreview { timelineRoomInfo = aTimelineRoomInfo(isDm = isDm), event = aTimelineItemEvent( content = content, - readReceiptState = readReceiptState, + // Only display read receipts for the first item + readReceiptState = readReceiptState.removeFirstOrNull() ?: aTimelineItemReadReceipts(), ), content = content, - // Render read receipts for the first item only - renderReadReceipts = !isDm && - callIntent == CallIntent.AUDIO && - state == RtcNotificationState.Started, isLastOutgoingMessage = false, onLongClick = {}, onReadReceiptsClick = {}, 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 936028cbd6..1f7f127be7 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 @@ -142,7 +142,6 @@ fun TimelineItemEventRow( timelineMode: Timeline.Mode, timelineRoomInfo: TimelineRoomInfo, timelineProtectionState: TimelineProtectionState, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, displayThreadSummaries: Boolean, onEventClick: () -> Unit, @@ -296,7 +295,6 @@ fun TimelineItemEventRow( isLastOutgoingMessage = isLastOutgoingMessage, receipts = event.readReceiptState.receipts, ), - renderReadReceipts = renderReadReceipts, onReadReceiptsClick = { onReadReceiptClick(event) }, modifier = Modifier.padding(top = 4.dp) ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt index f46ba780f3..6ea1161605 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowWithRRPreview.kt @@ -37,7 +37,6 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), - renderReadReceipts = true, isLastOutgoingMessage = false, ) // A message from current user @@ -49,7 +48,6 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), - renderReadReceipts = true, isLastOutgoingMessage = false, ) // Another message from current user @@ -61,7 +59,6 @@ internal fun TimelineItemEventRowWithRRPreview( timelineItemReactions = aTimelineItemReactions(count = 0), readReceiptState = TimelineItemReadReceipts(state.receipts), ), - renderReadReceipts = true, isLastOutgoingMessage = true, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 12f7a52f99..b41e66ae82 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -43,7 +43,6 @@ fun TimelineItemGroupedEventsRow( timelineMode: Timeline.Mode, timelineRoomInfo: TimelineRoomInfo, timelineProtectionState: TimelineProtectionState, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, focusedEventId: EventId?, displayThreadSummaries: Boolean, @@ -89,7 +88,6 @@ fun TimelineItemGroupedEventsRow( timelineRoomInfo = timelineRoomInfo, timelineProtectionState = timelineProtectionState, focusedEventId = focusedEventId, - renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, displayThreadSummaries = displayThreadSummaries, onClick = onClick, @@ -117,7 +115,6 @@ private fun TimelineItemGroupedEventsRowContent( timelineRoomInfo: TimelineRoomInfo, timelineProtectionState: TimelineProtectionState, focusedEventId: EventId?, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, displayThreadSummaries: Boolean, onClick: (TimelineItem.Event) -> Unit, @@ -173,7 +170,6 @@ private fun TimelineItemGroupedEventsRowContent( timelineItem = subGroupEvent, timelineRoomInfo = timelineRoomInfo, timelineProtectionState = timelineProtectionState, - renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, focusedEventId = focusedEventId, displayThreadSummaries = displayThreadSummaries, @@ -193,14 +189,13 @@ private fun TimelineItemGroupedEventsRowContent( ) } } - } else if (renderReadReceipts) { + } else if (timelineItem.aggregatedReadReceipts.isNotEmpty()) { TimelineItemReadReceiptView( state = ReadReceiptViewState( sendState = null, isLastOutgoingMessage = false, receipts = timelineItem.aggregatedReadReceipts, ), - renderReadReceipts = true, onReadReceiptsClick = onExpandGroupClick ) } @@ -219,7 +214,6 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi timelineRoomInfo = aTimelineRoomInfo(), timelineProtectionState = aTimelineProtectionState(), focusedEventId = events.events.first().eventId, - renderReadReceipts = true, isLastOutgoingMessage = false, displayThreadSummaries = false, onClick = {}, @@ -247,7 +241,6 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi timelineRoomInfo = aTimelineRoomInfo(), timelineProtectionState = aTimelineProtectionState(), focusedEventId = null, - renderReadReceipts = true, isLastOutgoingMessage = false, displayThreadSummaries = false, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 5dc69bba8b..0229d2220b 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 @@ -56,7 +56,6 @@ internal fun TimelineItemRow( timelineItem: TimelineItem, timelineMode: Timeline.Mode, timelineRoomInfo: TimelineRoomInfo, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, timelineProtectionState: TimelineProtectionState, focusedEventId: EventId?, @@ -114,7 +113,6 @@ internal fun TimelineItemRow( is TimelineItemStateContent, is TimelineItemLegacyCallInviteContent -> { TimelineItemStateEventRow( event = timelineItem, - renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, onClick = { onContentClick(timelineItem) }, onReadReceiptsClick = onReadReceiptClick, @@ -127,7 +125,6 @@ internal fun TimelineItemRow( timelineRoomInfo = timelineRoomInfo, event = timelineItem, content = timelineItem.content, - renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, onLongClick = onLongClick, onReadReceiptsClick = onReadReceiptClick, @@ -167,7 +164,6 @@ internal fun TimelineItemRow( event = timelineItem, timelineMode = timelineMode, timelineRoomInfo = timelineRoomInfo, - renderReadReceipts = renderReadReceipts, timelineProtectionState = timelineProtectionState, isLastOutgoingMessage = isLastOutgoingMessage, displayThreadSummaries = displayThreadSummaries, @@ -196,7 +192,6 @@ internal fun TimelineItemRow( timelineMode = timelineMode, timelineRoomInfo = timelineRoomInfo, timelineProtectionState = timelineProtectionState, - renderReadReceipts = renderReadReceipts, isLastOutgoingMessage = isLastOutgoingMessage, focusedEventId = focusedEventId, displayThreadSummaries = displayThreadSummaries, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt index 6e409f2b8d..db32b7a524 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemStateEventRow.kt @@ -39,7 +39,6 @@ import kotlinx.collections.immutable.persistentListOf @Composable fun TimelineItemStateEventRow( event: TimelineItem.Event, - renderReadReceipts: Boolean, isLastOutgoingMessage: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, @@ -86,7 +85,6 @@ fun TimelineItemStateEventRow( isLastOutgoingMessage = isLastOutgoingMessage, receipts = event.readReceiptState.receipts, ), - renderReadReceipts = renderReadReceipts, onReadReceiptsClick = { onReadReceiptsClick(event) }, ) } @@ -104,7 +102,6 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview { receipts = persistentListOf(aReadReceiptData(0)), ) ), - renderReadReceipts = true, isLastOutgoingMessage = false, onClick = {}, onLongClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index b03aaf01c1..037d9ea45d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -53,27 +53,24 @@ import kotlinx.collections.immutable.ImmutableList @Composable fun TimelineItemReadReceiptView( state: ReadReceiptViewState, - renderReadReceipts: Boolean, onReadReceiptsClick: () -> Unit, modifier: Modifier = Modifier, ) { if (state.receipts.isNotEmpty()) { - if (renderReadReceipts) { - ReadReceiptsRow( - modifier = modifier.clearAndSetSemantics { - hideFromAccessibility() - } - ) { - ReadReceiptsAvatars( - receipts = state.receipts, - modifier = Modifier - .clip(RoundedCornerShape(4.dp)) - .clickable { - onReadReceiptsClick() - } - .padding(2.dp) - ) + ReadReceiptsRow( + modifier = modifier.clearAndSetSemantics { + hideFromAccessibility() } + ) { + ReadReceiptsAvatars( + receipts = state.receipts, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .clickable { + onReadReceiptsClick() + } + .padding(2.dp) + ) } } else { when (state.sendState) { @@ -209,7 +206,6 @@ internal fun TimelineItemReadReceiptViewPreview( ) = ElementPreview { TimelineItemReadReceiptView( state = state, - renderReadReceipts = true, onReadReceiptsClick = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 7b369fe6b7..1679a6030a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -64,22 +64,24 @@ class TimelineItemsFactory( suspend fun replaceWith( timelineItems: List, roomMembers: List, + renderReadReceipts: Boolean, ) = withContext(dispatchers.computation) { lock.withLock { diffCacheUpdater.updateWith(timelineItems) - buildAndEmitTimelineItemStates(timelineItems, roomMembers) + buildAndEmitTimelineItemStates(timelineItems, roomMembers, renderReadReceipts) } } private suspend fun buildAndEmitTimelineItemStates( timelineItems: List, roomMembers: List, + renderReadReceipts: Boolean, ) { val newTimelineItemStates = ArrayList() for (index in diffCache.indices().reversed()) { val cacheItem = diffCache.get(index) if (cacheItem == null) { - buildAndCacheItem(timelineItems, index, roomMembers)?.also { timelineItemState -> + buildAndCacheItem(timelineItems, index, roomMembers, renderReadReceipts)?.also { timelineItemState -> newTimelineItemStates.add(timelineItemState) } } else { @@ -87,7 +89,8 @@ class TimelineItemsFactory( eventItemFactory.update( timelineItem = cacheItem, receivedMatrixTimelineItem = timelineItems[index] as MatrixTimelineItem.Event, - roomMembers = roomMembers + roomMembers = roomMembers, + renderReadReceipts = renderReadReceipts, ) } else { cacheItem @@ -103,10 +106,11 @@ class TimelineItemsFactory( timelineItems: List, index: Int, roomMembers: List, + renderReadReceipts: Boolean, ): TimelineItem? { val timelineItem = when (val currentTimelineItem = timelineItems[index]) { - is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers) + is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers, renderReadReceipts) is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem) MatrixTimelineItem.Other -> null } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index cf515a0b51..a36755dc09 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -57,6 +57,7 @@ class TimelineItemEventFactory( index: Int, timelineItems: List, roomMembers: List, + renderReadReceipts: Boolean, ): TimelineItem.Event { val currentSender = currentTimelineItem.event.sender val groupPosition = @@ -116,7 +117,7 @@ class TimelineItemEventFactory( sentDate = sentDate, groupPosition = groupPosition, reactionsState = currentTimelineItem.computeReactionsState(), - readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembers), + readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembers, renderReadReceipts), localSendState = currentTimelineItem.event.localSendState, inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser), threadInfo = mappedThreadInfo, @@ -133,9 +134,10 @@ class TimelineItemEventFactory( timelineItem: TimelineItem.Event, receivedMatrixTimelineItem: MatrixTimelineItem.Event, roomMembers: List, + renderReadReceipts: Boolean, ): TimelineItem.Event { return timelineItem.copy( - readReceiptState = receivedMatrixTimelineItem.computeReadReceiptState(roomMembers) + readReceiptState = receivedMatrixTimelineItem.computeReadReceiptState(roomMembers, renderReadReceipts) ) } @@ -180,8 +182,9 @@ class TimelineItemEventFactory( private fun MatrixTimelineItem.Event.computeReadReceiptState( roomMembers: List, + renderReadReceipts: Boolean, ): TimelineItemReadReceipts { - if (!config.computeReadReceipts) { + if (!config.computeReadReceipts || !renderReadReceipts) { return TimelineItemReadReceipts(receipts = persistentListOf()) } return TimelineItemReadReceipts( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index be44c64a5a..100eb1ca26 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -238,7 +238,6 @@ class MessagesViewTest { val eventsRecorder = EventsRecorder() val state = aMessagesState( timelineState = aTimelineState( - renderReadReceipts = true, timelineItems = persistentListOf( aTimelineItemEvent( readReceiptState = aTimelineItemReadReceipts( From 5a4c0b5d58e5af2de3afa7a59a80f4852ffb7cb8 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:33:40 +0100 Subject: [PATCH 356/471] Sync Strings from Localazy (#6986) Co-authored-by: bmarty <3940906+bmarty@users.noreply.github.com> --- .../src/main/res/values-ko/translations.xml | 3 +- .../src/main/res/values-ja/translations.xml | 4 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-ko/translations.xml | 4 + .../src/main/res/values-ko/translations.xml | 1 + .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-ko/translations.xml | 3 +- .../src/main/res/values-pl/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 6 + .../src/main/res/values-fi/translations.xml | 9 + .../src/main/res/values-hu/translations.xml | 13 + .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-ko/translations.xml | 6 + .../src/main/res/values-pl/translations.xml | 6 + .../src/main/res/values-zh/translations.xml | 6 + .../src/main/res/values-fi/translations.xml | 10 + .../src/main/res/values-hu/translations.xml | 43 + .../src/main/res/values-ja/translations.xml | 11 +- .../src/main/res/values-ko/translations.xml | 51 + .../src/main/res/values-fi/translations.xml | 2 + .../src/main/res/values-hu/translations.xml | 2 + .../src/main/res/values-ja/translations.xml | 2 + .../src/main/res/values-ko/translations.xml | 2 + .../src/main/res/values-pl/translations.xml | 2 + .../src/main/res/values-ko/translations.xml | 1 + .../src/main/res/values-ja/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 10 +- .../src/main/res/values-ko/translations.xml | 2 + .../src/main/res/values-ko/translations.xml | 1 + .../src/main/res/values-cs/translations.xml | 3 + .../src/main/res/values-el/translations.xml | 1 + .../src/main/res/values-et/translations.xml | 2 + .../src/main/res/values-fi/translations.xml | 4 + .../src/main/res/values-hu/translations.xml | 5 + .../src/main/res/values-ja/translations.xml | 4 +- .../src/main/res/values-ko/translations.xml | 27 + .../src/main/res/values-pl/translations.xml | 3 + .../src/main/res/values-zh/translations.xml | 2 + ....roomlist_RoomListContextMenu_Day_0_de.png | 4 +- ....roomlist_RoomListContextMenu_Day_1_de.png | 4 +- ....roomlist_RoomListContextMenu_Day_2_de.png | 4 +- .../features.home.impl_HomeView_Day_6_de.png | 4 +- .../features.home.impl_HomeView_Day_7_de.png | 4 +- .../features.home.impl_HomeView_Day_8_de.png | 4 +- ....impl.screens.error_ErrorView_Day_3_de.png | 4 +- ...nderingMapsNotSupportedDialog_Day_0_de.png | 3 + ...on.impl.show_ShowLocationView_Day_6_de.png | 4 +- ...on.impl.show_ShowLocationView_Day_7_de.png | 4 +- ...on.impl.show_ShowLocationView_Day_8_de.png | 3 - ...onlist_ActionListViewContent_Day_10_de.png | 4 +- ...onlist_ActionListViewContent_Day_11_de.png | 4 +- ...onlist_ActionListViewContent_Day_12_de.png | 4 +- ...ionlist_ActionListViewContent_Day_2_de.png | 4 +- ...ionlist_ActionListViewContent_Day_3_de.png | 4 +- ...ionlist_ActionListViewContent_Day_4_de.png | 4 +- ...ionlist_ActionListViewContent_Day_5_de.png | 4 +- ...ionlist_ActionListViewContent_Day_6_de.png | 4 +- ...ionlist_ActionListViewContent_Day_7_de.png | 4 +- ...ionlist_ActionListViewContent_Day_8_de.png | 4 +- ...ionlist_ActionListViewContent_Day_9_de.png | 4 +- ...eeditor_AttachmentImageEditorView_0_de.png | 4 +- ...eeditor_AttachmentImageEditorView_1_de.png | 4 +- ...eeditor_AttachmentImageEditorView_2_de.png | 4 +- ...eeditor_AttachmentImageEditorView_3_de.png | 4 +- ...eeditor_AttachmentImageEditorView_4_de.png | 4 +- ...eeditor_AttachmentImageEditorView_5_de.png | 4 +- ...eeditor_AttachmentImageEditorView_6_de.png | 3 + ...eeditor_AttachmentImageEditorView_7_de.png | 3 + ...ts.preview_AttachmentsPreviewView_6_de.png | 4 +- ...er_AttachmentSourcePickerMenu_Day_0_de.png | 4 +- ...d.list_PinnedMessagesListView_Day_3_de.png | 4 +- ...es.impl.timeline_TimelineView_Day_4_de.png | 4 +- ...es.impl.timeline_TimelineView_Day_6_de.png | 4 +- ...es.messages.impl_MessagesView_Day_1_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_0_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_10_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_11_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_12_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_13_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_14_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_15_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_16_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_17_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_18_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_19_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_1_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_20_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_21_de.png | 4 +- ...roomdetails.impl_RoomDetailsDark_22_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_2_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_3_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_4_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_5_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_6_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_7_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_8_de.png | 4 +- ....roomdetails.impl_RoomDetailsDark_9_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_0_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_10_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_11_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_12_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_13_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_14_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_15_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_16_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_17_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_18_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_19_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_1_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_20_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_21_de.png | 4 +- ...res.roomdetails.impl_RoomDetails_22_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_2_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_3_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_4_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_5_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_6_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_7_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_8_de.png | 4 +- ...ures.roomdetails.impl_RoomDetails_9_de.png | 4 +- ...impl_RoomMemberModerationView_Day_0_de.png | 4 +- ...impl_RoomMemberModerationView_Day_1_de.png | 4 +- ...impl_RoomMemberModerationView_Day_2_de.png | 4 +- ...impl_RoomMemberModerationView_Day_3_de.png | 4 +- ...rofile.shared_UserProfileView_Day_2_de.png | 4 +- ...rofile.shared_UserProfileView_Day_5_de.png | 4 +- ...nents_AvatarActionBottomSheet_Day_0_de.png | 4 +- ...tails_MediaDetailsBottomSheet_Day_0_de.png | 4 +- ...tails_MediaDetailsBottomSheet_Day_1_de.png | 4 +- ...tails_MediaDetailsBottomSheet_Day_2_de.png | 4 +- ...tails_MediaDetailsBottomSheet_Day_3_de.png | 4 +- ...tails_MediaDetailsBottomSheet_Day_4_de.png | 3 + ...impl.gallery_MediaGalleryView_Day_8_de.png | 4 +- ...impl.local.file_MediaFileView_Day_0_de.png | 3 + ...impl.local.file_MediaFileView_Day_1_de.png | 3 + ...impl.local.file_MediaFileView_Day_2_de.png | 3 + ....viewer_MediaViewerViewLandscape_11_de.png | 4 +- ...l.viewer_MediaViewerViewLandscape_6_de.png | 3 + ...l.viewer_MediaViewerViewLandscape_7_de.png | 3 + ...ewer.impl.viewer_MediaViewerView_11_de.png | 4 +- ...iewer.impl.viewer_MediaViewerView_6_de.png | 3 + ...iewer.impl.viewer_MediaViewerView_7_de.png | 3 + screenshots/html/data.js | 2179 +++++++++-------- 144 files changed, 1555 insertions(+), 1291 deletions(-) create mode 100644 screenshots/de/features.location.api_RenderingMapsNotSupportedDialog_Day_0_de.png delete mode 100644 screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_de.png create mode 100644 screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_6_de.png create mode 100644 screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_7_de.png diff --git a/features/deactivation/impl/src/main/res/values-ko/translations.xml b/features/deactivation/impl/src/main/res/values-ko/translations.xml index 42dd0aa0e2..83eaf7ff21 100644 --- a/features/deactivation/impl/src/main/res/values-ko/translations.xml +++ b/features/deactivation/impl/src/main/res/values-ko/translations.xml @@ -3,11 +3,12 @@ "계정을 비활성화하시겠습니까? 이 작업은 되돌릴 수 없습니다." "모든 내 메시지 삭제" "경고: 향후 사용자는 불완전한 대화 내용을 볼 수 있습니다." - "계정을 비활성화하는 것은 %1$s 이며, 다음과 같은 조치를 취합니다:" + "계정을 삭제하면 %1$s, 다음과 같이 처리됩니다:" "불가역적" "%1$s 귀하의 계정 (로그인할 수 없으며, 귀하의 ID는 재사용할 수 없습니다)." "영구적으로 비활성화" "모든 채팅방에서 자신을 제거하세요." "당사의 신원 서버에서 귀하의 계정 정보를 삭제하세요." "메시지는 등록된 사용자에게는 계속 표시되지만, 삭제하면 신규 또는 미등록 사용자는 볼 수 없게 됩니다." + "계정 삭제" diff --git a/features/home/impl/src/main/res/values-ja/translations.xml b/features/home/impl/src/main/res/values-ja/translations.xml index c1c1ade1ff..855bb14533 100644 --- a/features/home/impl/src/main/res/values-ja/translations.xml +++ b/features/home/impl/src/main/res/values-ja/translations.xml @@ -1,7 +1,7 @@ "すべての通知を確実に受信するために、このアプリのバッテリー最適化を無効にしてください。" - "最適化を無効にする" + "無効にする" "通知が届いていませんか?" "通知音が更新され、より明確で速く、そして邪魔にならなくなりました。" "サウンドを刷新しました" @@ -38,7 +38,7 @@ "低い優先度のチャットはまだありません" "フィルターを解除して他のチャットを表示できます" "この選択中にチャットがありません" - "人々" + "ユーザー" "まだダイレクトメッセージは届いていません" "ルーム" "まだルームに参加していません" diff --git a/features/home/impl/src/main/res/values-sk/translations.xml b/features/home/impl/src/main/res/values-sk/translations.xml index 12e6284468..aab1f6dc1d 100644 --- a/features/home/impl/src/main/res/values-sk/translations.xml +++ b/features/home/impl/src/main/res/values-sk/translations.xml @@ -6,7 +6,7 @@ "Vaše oznámenia boli aktualizované – sú prehľadnejšie, rýchlejšie a menej rušivé." "Obnovili sme vaše zvuky" "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" + "Získať kľúč na obnovenie" "Nastaviť obnovenie" "Potvrďte svoj kľúč na obnovenie, aby ste zachovali prístup k úložisku kľúčov a histórii správ." "Zadajte kľúč na obnovenie" diff --git a/features/invitepeople/impl/src/main/res/values-ko/translations.xml b/features/invitepeople/impl/src/main/res/values-ko/translations.xml index 19214c5a79..421f6d5e02 100644 --- a/features/invitepeople/impl/src/main/res/values-ko/translations.xml +++ b/features/invitepeople/impl/src/main/res/values-ko/translations.xml @@ -2,4 +2,8 @@ "이미 회원" "이미 초대됨" + "이 사용자들과의 대화 내역이 없습니다. 계속하려면 먼저 이 방으로의 초대를 확인해 주세요." + "이 사용자와의 대화 내역이 없습니다. 계속하려면 먼저 이 방으로의 초대를 확인해 주세요." + "이 방에 새 사용자를 초대할까요?" + "이 방에 새 사용자를 초대할까요?" diff --git a/features/location/impl/src/main/res/values-ko/translations.xml b/features/location/impl/src/main/res/values-ko/translations.xml index e283d20b9c..aa23450335 100644 --- a/features/location/impl/src/main/res/values-ko/translations.xml +++ b/features/location/impl/src/main/res/values-ko/translations.xml @@ -2,4 +2,5 @@ "실시간 위치 기록은 대화방에 저장되며, 공유 종료 후에도 멤버들이 확인할 수 있습니다." "실시간 위치를 공유할 시간을 선택해 주세요." + "이 방에서는 실시간 위치를 공유할 권한이 없습니다." diff --git a/features/lockscreen/impl/src/main/res/values-sk/translations.xml b/features/lockscreen/impl/src/main/res/values-sk/translations.xml index 14687f9272..e8fdf50bfd 100644 --- a/features/lockscreen/impl/src/main/res/values-sk/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-sk/translations.xml @@ -23,7 +23,7 @@ Vyberte si niečo zapamätateľné. Ak tento kód PIN zabudnete, budete z aplik "Zadajte prosím ten istý PIN dvakrát" "PIN kódy sa nezhodujú" "Ak chcete pokračovať, musíte sa znovu prihlásiť a vytvoriť nový PIN kód." - "Prebieha odhlasovanie" + "Toto zariadenie sa odstraňuje" "Máte %1$d pokus na odomknutie" "Máte %1$d pokusy na odomknutie" diff --git a/features/login/impl/src/main/res/values-ko/translations.xml b/features/login/impl/src/main/res/values-ko/translations.xml index 338ba628c9..8f313b15ee 100644 --- a/features/login/impl/src/main/res/values-ko/translations.xml +++ b/features/login/impl/src/main/res/values-ko/translations.xml @@ -28,7 +28,7 @@ "서버의 주소는 무엇인가요?" "서버 선택" "계정 만들기" - "계정이 비활성화되었습니다." + "삭제된 계정입니다." "잘못된 아이디/비밀번호" "이 사용자 ID는 유효하지 않습니다. 예상 형식: ‘@user:homeserver.org’" "이 서버는 새로 고침 토큰을 사용하도록 구성되어 있습니다. 비밀번호 기반 로그인을 사용하는 경우 이 기능은 지원되지 않습니다." @@ -45,6 +45,7 @@ "%1$s(으)로 돌아가기" "%1$s(으)로 진행하기 전에 키 저장소를 활성화해 주세요." "버전 %1$s" + "계정 확인 중" "수동으로 로그인" "%1$s 에 로그인합니다" "QR 코드로 로그인" 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 ca0d035fe0..634d856cdf 100644 --- a/features/login/impl/src/main/res/values-pl/translations.xml +++ b/features/login/impl/src/main/res/values-pl/translations.xml @@ -97,7 +97,7 @@ Spróbuj zalogować się ręcznie lub zeskanuj kod QR na innym urządzeniu.""Zacznij od nowa" "Wystąpił nieoczekiwany błąd. Spróbuj ponownie." "Oczekiwanie na drugie urządzenie" - "Twój dostawca konta może poprosić o podany kod, aby zweryfikować logowanie." + "Twój dostawca konta może poprosić o podany kod w celu weryfikacji logowania." "Twój kod weryfikacyjny" "Zmień dostawcę konta" "Serwer prywatny dla pracowników Element." diff --git a/features/logout/impl/src/main/res/values-sk/translations.xml b/features/logout/impl/src/main/res/values-sk/translations.xml index 4fe07b2fa7..28e0a05c2f 100644 --- a/features/logout/impl/src/main/res/values-sk/translations.xml +++ b/features/logout/impl/src/main/res/values-sk/translations.xml @@ -12,7 +12,7 @@ "Vaše kľúče sa ešte stále zálohujú" "Odstrániť toto zariadenie" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam." - "Obnovenie nie je nastavené" + "Čoskoro stratíte prístup k svojim zašifrovaným konverzáciám" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, môžete stratiť prístup k svojim šifrovaným správam." "Uložili ste si kľúč na obnovenie?" diff --git a/features/messages/impl/src/main/res/values-et/translations.xml b/features/messages/impl/src/main/res/values-et/translations.xml index aa5a9045d1..86b1995f32 100644 --- a/features/messages/impl/src/main/res/values-et/translations.xml +++ b/features/messages/impl/src/main/res/values-et/translations.xml @@ -16,6 +16,12 @@ "Reisimine ja kohad" "Hiljutised emojid" "Sümbolid" + "Pööra pilti ümber rõhttelje" + "Pilt on pööratud ümber rõhttelje" + "Pildi algne variant" + "Pööra pilti ümber püsttelje" + "Pilt on pööratud ümber püsttelje" + "Pildi algne variant" "Pööra pilti vasakule" "%1$d kraad" diff --git a/features/messages/impl/src/main/res/values-fi/translations.xml b/features/messages/impl/src/main/res/values-fi/translations.xml index 25f434ec25..881a5386e7 100644 --- a/features/messages/impl/src/main/res/values-fi/translations.xml +++ b/features/messages/impl/src/main/res/values-fi/translations.xml @@ -16,10 +16,18 @@ "Matkustaminen ja paikat" "Viimeaikaiset emojit" "Symbolit" + "Käännä kuva vaakasuunnassa" + "Käännetty vaakasuunnassa" + "Alkuperäinen" + "Käännä kuva pystysuunnassa" + "Käännetty pystysuunnassa" + "Alkuperäinen" + "Kierrä kuvaa vasemmalle" "%1$d aste" "%1$d astetta" + "Muokkaa kuvaa" "Kuvatekstit eivät välttämättä näy ihmisille, jotka käyttävät vanhempia sovelluksia." "Napauta muuttaaksesi videon lähetyslaatua" "Tiedostoa ei voitu lähettää." @@ -30,6 +38,7 @@ "Kohde %1$d / %2$d" "Optimoi kuvanlaatu" "Käsitellään…" + "Lisää mediaa" "Estä käyttäjä" "Valitse tämä, jos haluat piilottaa kaikki nykyiset ja tulevat viestit tältä käyttäjältä" "Tämä viesti ilmoitetaan kotipalvelimesi ylläpitäjälle. Ylläpitäjä ei pysty lukemaan salattuja viestejä." diff --git a/features/messages/impl/src/main/res/values-hu/translations.xml b/features/messages/impl/src/main/res/values-hu/translations.xml index 3cbd86e8d1..bb33520cb6 100644 --- a/features/messages/impl/src/main/res/values-hu/translations.xml +++ b/features/messages/impl/src/main/res/values-hu/translations.xml @@ -16,6 +16,18 @@ "Utazás és helyek" "Legutóbbi emodzsik" "Szimbólumok" + "Kép vízszintes tükrözése" + "Vízszintesen tükrözve" + "Eredeti" + "Kép függőleges tükrözése" + "Függőlegesen tükrözve" + "Eredeti" + "Kép elforgatása balra" + + "%1$d fok" + "%1$d fok" + + "Fotó szerkesztése" "Előfordulhat, hogy a feliratok nem láthatók a régebbi alkalmazásokat használók számára." "Koppintson a feltöltött videók minőségének módosításához" "A fájl nem tölthető fel." @@ -26,6 +38,7 @@ "%1$d. elem / %2$d" "Képminőség optimalizációja" "Feldolgozás…" + "Média hozzáadása" "Felhasználó letiltása" "Jelölje be, ha el akarja rejteni az összes jelenlegi és jövőbeli üzenetet ettől a felhasználótól" "Ez az üzenet jelentve lesz a Matrix-kiszolgáló adminisztrátorának. Nem fogja tudni elolvasni a titkosított üzeneteket." diff --git a/features/messages/impl/src/main/res/values-ja/translations.xml b/features/messages/impl/src/main/res/values-ja/translations.xml index f1a4bde02f..455996fd1a 100644 --- a/features/messages/impl/src/main/res/values-ja/translations.xml +++ b/features/messages/impl/src/main/res/values-ja/translations.xml @@ -39,7 +39,7 @@ "カメラ" "写真を撮影" "動画を撮影" - "添付ファイル" + "ファイルを添付" "アルバムの写真と動画" "場所を共有" "投票" diff --git a/features/messages/impl/src/main/res/values-ko/translations.xml b/features/messages/impl/src/main/res/values-ko/translations.xml index daede9ec1d..fbcb0cab52 100644 --- a/features/messages/impl/src/main/res/values-ko/translations.xml +++ b/features/messages/impl/src/main/res/values-ko/translations.xml @@ -16,6 +16,11 @@ "여행 & 장소" "최근 이모지" "상징" + "이미지를 왼쪽으로 회전" + + "%1$d도" + + "사진 편집" "캡션은 오래된 앱을 사용하는 사용자에게 표시되지 않을 수 있습니다." "비디오 업로드 품질을 변경하려면 탭하세요" "파일을 업로드할 수 없습니다." @@ -26,6 +31,7 @@ "전체 %2$d개 중 %1$d번째 파일" "이미지 품질 최적화" "처리 중…" + "미디어 추가" "사용자 차단하기" "이 사용자의 현재 및 향후 모든 메시지를 숨기려면 확인하세요." "이 메시지는 홈서버의 관리자에게 보고되었습니다. 암호화된 메시지는 읽을 수 없습니다." diff --git a/features/messages/impl/src/main/res/values-pl/translations.xml b/features/messages/impl/src/main/res/values-pl/translations.xml index 1a9dc12f1c..d8ffaac54c 100644 --- a/features/messages/impl/src/main/res/values-pl/translations.xml +++ b/features/messages/impl/src/main/res/values-pl/translations.xml @@ -16,6 +16,12 @@ "Podróż i miejsca" "Ostatnie emoji" "Symbole" + "Odwróć obraz w poziomie" + "Odwrócono w poziomie" + "Oryginalny" + "Odwróć obraz w pionie" + "Odwrócono w pionie" + "Oryginalny" "Obróć obraz w lewo" "%1$d stopień" diff --git a/features/messages/impl/src/main/res/values-zh/translations.xml b/features/messages/impl/src/main/res/values-zh/translations.xml index c572b737f1..e2300912ae 100644 --- a/features/messages/impl/src/main/res/values-zh/translations.xml +++ b/features/messages/impl/src/main/res/values-zh/translations.xml @@ -16,6 +16,12 @@ "文旅景点" "最近的 Emoji" "符号" + "水平翻转图像" + "水平翻转" + "原始" + "垂直翻转图像" + "垂直翻转" + "原始" "向左旋转图像" "%1$d 度" diff --git a/features/preferences/impl/src/main/res/values-fi/translations.xml b/features/preferences/impl/src/main/res/values-fi/translations.xml index 99f900bcc1..f37673427f 100644 --- a/features/preferences/impl/src/main/res/values-fi/translations.xml +++ b/features/preferences/impl/src/main/res/values-fi/translations.xml @@ -58,6 +58,7 @@ "Kokeilunhaluinen olo?" "Labrat" "Lisäasetukset" + "Puhelun soittoääni" "Ääni- ja videopuheluista" "Konfiguraatio ei täsmää" "Olemme yksinkertaistaneet ilmoitusasetuksia, jotta vaihtoehdot olisi helpompi löytää. Joitakin aiemmin valitsemiasi asetuksia ei näytetä tässä, mutta ne ovat edelleen voimassa. @@ -76,17 +77,26 @@ Jos jatkat, jotkin asetukset saattavat muuttua." "Kutsut" "Kotipalvelimesi ei tue tätä vaihtoehtoa salatuissa huoneissa, joten et ehkä saa ilmoitusta joissakin huoneissa." "Maininnat" + "Valitse toinen ääni…" + "Käytössä tällä hetkellä %1$s" + "Viestin ääni" + "Viestin ääni" "Kaikki" "Maininnat" "Ilmoita minulle" "Ilmoita minulle @room-maininnoista" + "Mukautettu" "Mukautettu ääni…" "Virhe tiedoston poistamisessa" "Elementin oletus" + "Element Fade" "Virhe tiedoston tuonnissa" "Ongelma ilmoitusäänen esikatselussa" "Ääni" + "Hylkää hälytysäänen virhe" "Ongelma ilmoitusäänen asettamisessa" + "Hiljainen" + "Järjestelmän oletus" "Jos haluat saada ilmoituksia, vaihda %1$s." "järjestelmäsi asetuksia" "Järjestelmän ilmoitukset on poissa päältä" 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 4c1b988696..febc0e02a9 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -58,6 +58,7 @@ "Kísérletezni szeretne?" "Kísérletek" "További beállítások" + "Csengőhang" "Hang- és videóhívások" "Konfigurációs eltérés" "Egyszerűsítettük az értesítési beállításokat, hogy könnyebben megtalálhatók legyenek a lehetőségek. A korábban kiválasztott egyéni beállítások némelyike nem jelenik meg itt, de továbbra is aktív. @@ -76,10 +77,52 @@ Ha folytatja, egyes beállítások megváltozhatnak." "Meghívók" "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, előfordulhat, hogy egyes szobákban nem kap értesítést." "Említések" + "Válasszon egy másik hangot…" + "Jelenleg használt: %1$s" + "Üzenet hang" + "Üzenet hang" "Összes" "Említések" "Értesítés ezekről:" "Értesítés a @room említésekor" + "Egyedi" + "Egyedi hang…" + "Hiba a fájl törlésekor" + "Element alapértelmezett" + "Element Fade" + "Hiba a fájl importálásakor" + "Probléma a riasztási hang előnézetekor" + "Hang" + "Figyelmeztető hang hiba kikapcsolása" + "Probléma a riasztási hang beállításánál" + "Néma" + "Riasztás" + "Előrejelzés" + "Harang" + "Virágzás" + "Calypso" + "Chime" + "Choo Choo" + "Rendszer alapértelmezett" + "Lejtő" + "Elektronikus" + "Harsonaszó" + "Üveg" + "Kürt" + "Létra" + "Menüett" + "Hírvillám" + "Noir" + "Sherwood-erdő" + "Varázslat" + "Feszültség" + "Swish" + "Távíró" + "Lábujjhegy" + "Három hang" + "Csipog" + "Írógép" + "Frissítés" "Az értesítések fogadásához kérjük, módosítsa a %1$s." "rendszerbeállításokat" "A rendszerértesítések ki vannak kapcsolva" diff --git a/features/preferences/impl/src/main/res/values-ja/translations.xml b/features/preferences/impl/src/main/res/values-ja/translations.xml index 5cab6bdcf8..ea07a9d931 100644 --- a/features/preferences/impl/src/main/res/values-ja/translations.xml +++ b/features/preferences/impl/src/main/res/values-ja/translations.xml @@ -54,9 +54,10 @@ "スレッドへの返信を有効化" "変更を適用するためにアプリケーションは再起動します。" "開発段階の最新機能を試すことができます。未完成のため、変更や不安定な挙動が生じる可能性があります。" - "探究したいですか?" + "探究的な気分ですか?" "ラボ" "追加設定" + "発信音" "音声・ビデオ通話" "設定の不一致" "通知設定を簡素化し、見つけやすくしました。以前のカスタム設定の一部はここに表示されませんが、引き続き有効です。 @@ -75,10 +76,15 @@ "招待" "暗号化されたルームでは、この機能にホームサーバーが対応しないため、一部のルームから通知が届かない可能性があります。" "メンション" + "他の音を選択…" + "%1$s を使用中" + "メッセージの音" + "メッセージの音" "すべて" "メンション" "以下を通知" @ルームで通知を受け取る + "カスタム" "カスタム着信音" "ファイルの削除に失敗" "Element の既定" @@ -86,7 +92,9 @@ "ファイルの取り込みに失敗" "通知音のプレビューで問題が発生しました" "音" + "通知音のエラーを無視" "通知音の設定に問題が発生しました" + "サイレント" "アラート" "予感" "ベル" @@ -94,6 +102,7 @@ "カリプソ" "チャイム" "汽車ポッポ" + "システムのデフォルト" "下降" "エレクトロニック" "ファンファーレ" diff --git a/features/preferences/impl/src/main/res/values-ko/translations.xml b/features/preferences/impl/src/main/res/values-ko/translations.xml index e2c5e8e83b..2ab3673026 100644 --- a/features/preferences/impl/src/main/res/values-ko/translations.xml +++ b/features/preferences/impl/src/main/res/values-ko/translations.xml @@ -11,6 +11,13 @@ "방 초대 요청에서 아바타 숨기기" "타임라인에서 미디어 미리 보기 숨기기" "실험실" + "위치 업데이트 전환 기준 거리" + "이 앱에 \"정확한 위치\" 권한이 허용되어 있는지 확인해 주세요. 권한을 변경하려면 %1$s(으)로 이동하세요." + "앱 설정" + "실시간 위치 업데이트" + + "%1$d미터마다" + "사진과 동영상을 더 빠르게 업로드하고 데이터 사용량을 줄이세요" "미디어 품질 최적화" "중재와 안전" @@ -51,6 +58,7 @@ "새로운 것을 시도해보고 싶으신가요?" "실험실" "추가 설정" + "통화 벨소리" "음성 및 동영상 통화" "구성 불일치" "알림 설정을 간소화하여 옵션을 더 쉽게 찾을 수 있도록 했습니다. 과거에 선택한 일부 맞춤 설정은 여기에서 표시되지 않지만, 여전히 활성화되어 있습니다. @@ -69,14 +77,57 @@ "초대" "귀하의 홈서버는 암호화된 방에서 이 옵션을 지원하지 않으므로, 일부 방에서는 알림이 표시되지 않을 수 있습니다." "언급" + "다른 소리를 선택하세요…" + "현재 사용 중: %1$s" + "메시지 소리" + "메시지 소리" "모두" "언급" "나에게 알려주세요" @room 에서 알림 받기 + "사용자 지정" + "소리 사용자 지정" + "파일 삭제 오류" + "Element 기본 설정" + "엘리먼트 페이드" + "파일 가져오기 오류" + "알림음 미리듣기 문제 발생" + "소리" + "알림음 오류 해제" + "알림 소리 설정 문제" + "무음" + "알림" + "앤티시페이트" + "벨" + "블룸" + "칼립소" + "차임" + "칙칙폭폭" + "시스템 기본값" + "디센트" + "일렉트로닉" + "팡파레" + "글래스" + "혼" + "래더" + "미뉴에트" + "뉴스 플래시" + "느와르" + "셔우드 포레스트" + "스펠" + "서스펜스" + "스위시" + "텔레그래프" + "팁토즈" + "트라이톤" + "트윗" + "타자기" + "업데이트" "알림을 받으려면 %1$s 을 변경해 주세요." "시스템 설정" "시스템 알림이 꺼져 있습니다." "알림" + "검은색" "다크" "라이트" "시스템" diff --git a/features/rageshake/impl/src/main/res/values-fi/translations.xml b/features/rageshake/impl/src/main/res/values-fi/translations.xml index 1970d538fd..5590602367 100644 --- a/features/rageshake/impl/src/main/res/values-fi/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fi/translations.xml @@ -8,6 +8,8 @@ "Kuvaile ongelmaasi…" "Jos mahdollista, kirjoita englanniksi." "Kuvaus on liian lyhyt. Kerro tarkemmin mitä tapahtui, kiitos!" + "Voit kirjoittaa mahdollisen GitHub-issuen numeron." + "GitHub-issue" "Lähetä kaatumislokit" "Lähetä lokitiedostot" "Lokitiedostosi ovat liian suuria, joten niitä ei voida sisällyttää tähän raporttiin. Lähetä ne meille toisella tavalla." diff --git a/features/rageshake/impl/src/main/res/values-hu/translations.xml b/features/rageshake/impl/src/main/res/values-hu/translations.xml index 851d3f0067..db665a1253 100644 --- a/features/rageshake/impl/src/main/res/values-hu/translations.xml +++ b/features/rageshake/impl/src/main/res/values-hu/translations.xml @@ -8,6 +8,8 @@ "Írja le a problémát…" "Ha lehetséges, a leírást angolul írja meg." "A leírás túl rövid, adjon meg további részleteket a történtekről. Köszönjük!" + "Megadhatja egy kapcsolódó GitHub-probléma számát, ha van ilyen." + "GitHub-jegy" "Összeomlásnaplók küldése" "Naplók engedélyezése" "A naplófájlok túl nagyok, ezért nem szerepelhetnek ebben a jelentésben. Más módon küldje el őket." diff --git a/features/rageshake/impl/src/main/res/values-ja/translations.xml b/features/rageshake/impl/src/main/res/values-ja/translations.xml index 7c46d2beb5..53d25b364f 100644 --- a/features/rageshake/impl/src/main/res/values-ja/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ja/translations.xml @@ -8,6 +8,8 @@ "問題の説明…" "可能であれば英語で記入してください。" "説明が過度に短いです。問題についてより詳細にご記入ください。" + "関連したGitHubのissueがある場合はIDを記入してください。" + "GitHub issue" "クラッシュログを送信" "ログの記録を許可" "ログのサイズが大きく、報告に添付することができません。ログは別の方法で送信してください。" diff --git a/features/rageshake/impl/src/main/res/values-ko/translations.xml b/features/rageshake/impl/src/main/res/values-ko/translations.xml index ef4978924c..1b15ce3f5a 100644 --- a/features/rageshake/impl/src/main/res/values-ko/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ko/translations.xml @@ -8,6 +8,8 @@ "문제를 설명해 주세요…" "가능하다면 영어로 설명을 작성해 주십시오." "설명 내용이 너무 짧습니다. 발생한 상황에 대해 더 자세한 내용을 제공해 주시기 바랍니다. 감사합니다!" + "연관된 GitHub 이슈 번호가 있다면 입력할 수 있습니다." + "GitHub 이슈" "충돌 로그 보내기" "로그 허용" "귀하의 로그가 너무 커서 이 보고서에 포함할 수 없습니다. 다른 방법으로 보내주시기 바랍니다." diff --git a/features/rageshake/impl/src/main/res/values-pl/translations.xml b/features/rageshake/impl/src/main/res/values-pl/translations.xml index c433071584..2b0feb0851 100644 --- a/features/rageshake/impl/src/main/res/values-pl/translations.xml +++ b/features/rageshake/impl/src/main/res/values-pl/translations.xml @@ -8,6 +8,8 @@ "Opisz problem…" "Jeśli to możliwe, napisz zgłoszenje w języku angielskim." "Opis jest zbyt krótki, podaj więcej szczegółów na temat tego co się stało. Dzięki!" + "Możesz wpisać numer powiązanego zgłoszenia w serwisie GitHub, jeśli takie istnieje." + "Zgłoszenie Github" "Wyślij logi awarii" "Zezwól na logi" "Twoje dzienniki są zbyt duże, więc nie można ich uwzględnić w tym raporcie. Prześlij je do nas w inny sposób." diff --git a/features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml b/features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml index 4ad64f1723..c9de19a43a 100644 --- a/features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml +++ b/features/rolesandpermissions/impl/src/main/res/values-ko/translations.xml @@ -6,6 +6,7 @@ "메시지 삭제" "멤버" "사람 초대하기" + "실시간 위치 공유" "스페이스 관리" "방 관리" "멤버 관리" diff --git a/features/securebackup/impl/src/main/res/values-ja/translations.xml b/features/securebackup/impl/src/main/res/values-ja/translations.xml index 72c8c90809..b9612c4e09 100644 --- a/features/securebackup/impl/src/main/res/values-ja/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ja/translations.xml @@ -6,7 +6,7 @@ "鍵の保管庫" "チャットをバックアップするには、鍵の保管庫を使用する必要があります。" "この端末上の鍵をアップロードします" - "鍵の保管庫を使用します" + "鍵の保管庫を使用" "回復鍵を変更" "あなたのチャットはエンドツーエンド暗号化を使用して自動的にバックアップされています。すべての端末を使用できない状況で、このバックアップからデジタルIDを復元するには、回復鍵が必要となります。" "回復鍵を入力" 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 3e92a39df0..4e646ef47e 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -11,7 +11,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še úložisko kľúčov nie je momentálne synchronizované." - "Nastaviť obnovenie" + "Získať kľúč na obnovenie" "Otvoriť %1$s v stolnom počítači" "Znova sa prihláste do svojho účtu" "Keď sa zobrazí výzva na overenie vášho zariadenia, vyberte %1$s" @@ -25,10 +25,10 @@ "Budete musieť znova overiť všetky existujúce zariadenia a kontakty" "Obnovte svoju totožnosť iba vtedy, ak nemáte prístup k inému prihlásenému zariadeniu a stratili ste kľúč na obnovenie." "Znovu nastavte svoju totožnosť v prípade, že ju nemôžete potvrdiť iným spôsobom" - "Vypnúť" + "Odstrániť" "Ak odstránite všetky svoje zariadenia, stratíte svoju zašifrovanú históriu správ a budete si musieť obnoviť svoju digitálnu identitu." - "Ste si istí, že chcete vypnúť zálohovanie?" - "Vypnutím zálohovania sa odstráni aktuálna záloha šifrovacích kľúčov a vypnú sa ďalšie bezpečnostné funkcie. V tomto prípade:" + "Naozaj chcete odstrániť úložisko kľúčov?" + "Odstránením úložiska kľúčov sa zo servera odstránia vaše kľúče digitálnej identity a správ a vypnú sa nasledujúce bezpečnostné funkcie:" "Na nových zariadeniach nebudete mať zašifrovanú históriu správ" "Stratíte prístup k svojim zašifrovaným správam, ak sa odhlásite z aplikácie %1$s na všetkých zariadeniach" "Ste si istí, že chcete vypnúť zálohovanie?" @@ -58,7 +58,7 @@ "Vygenerujte si váš kľúč na obnovenie" "Nezdieľajte to s nikým!" "Úspešné nastavenie obnovy" - "Nastaviť obnovenie" + "Získať kľúč na obnovenie" "Áno, znovu nastaviť teraz" "Tento proces je nezvratný." "Naozaj chcete obnoviť svoje šifrovanie?" diff --git a/libraries/matrixui/src/main/res/values-ko/translations.xml b/libraries/matrixui/src/main/res/values-ko/translations.xml index 09d831e348..b36a61cf9b 100644 --- a/libraries/matrixui/src/main/res/values-ko/translations.xml +++ b/libraries/matrixui/src/main/res/values-ko/translations.xml @@ -3,5 +3,7 @@ "초대장 보내기" "%1$s 와 채팅을 시작하시겠습니까?" "초대장을 보내시겠습니까?" + "이 사용자와의 대화 내역이 없습니다. 계속하려면 먼저 초대를 확인해 주세요." + "이 사용자와 대화를 시작하시겠습니까?" "%1$s (%2$s) 당신을 초대했습니다" diff --git a/libraries/mediaviewer/impl/src/main/res/values-ko/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-ko/translations.xml index 3362171d27..0a14586e6c 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-ko/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-ko/translations.xml @@ -16,6 +16,7 @@ "파일 명" "더 이상 표시할 파일이 없습니다" "더 이상 보여줄 미디어가 없습니다" + "파일 정보" "에 의해 업로드됨" "에 업로드됨" 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 1bf3802b12..5f45bea93b 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -50,12 +50,15 @@ "Avatar místnosti" "Odeslat soubory" "Poloha odesílatele" + "Odesláno %1$s v %2$s" "Vyžaduje se časově omezená akce, na ověření máte jednu minutu" "Nastavení, vyžaduje akci" "Zobrazit heslo" "Zahájit hovor" "Zahájit videohovor" "Zahájit hlasový hovor" + "Vlákno v %1$s" + "Vlákna v %1$s" "Místnost s náhrobkem" "Avatar uživatele" "Uživatelské menu" 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 ec14b73e04..a04e7aceb6 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -50,6 +50,7 @@ "Απαιτείται ενέργεια περιορισμένης χρονικής διάρκειας, έχετε ένα λεπτό για επαλήθευση." "Εμφάνιση κωδικού πρόσβασης" "Ξεκίνησε μια κλήση" + "Ξεκινήστε μια βιντεοκλήση" "Έναρξη φωνητικής κλήσης" "Θαμένη αίθουσα" "Άβαταρ χρήστη" 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 fd45849e7d..ae9ee22038 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -545,4 +545,6 @@ Kas sa oled kindel, et soovid jätkata?" "Sul pole ligipääsu antud sõnumile" "Sõnumi dekrüptimine ei õnnestu" "Kuna seade on verifitseerimata või saatja pole sinu digitaalset identiteeti verifitseerinud, siis sõnumi näitamine on blokeeritud." + "Sinu nutiseadme tarkvara on liiga vana, vajalik on Android 8 või uuem." + "Kaartide joonistamine pole toetatud" diff --git a/libraries/ui-strings/src/main/res/values-fi/translations.xml b/libraries/ui-strings/src/main/res/values-fi/translations.xml index 88d8d712b0..096652fbd4 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -99,6 +99,7 @@ "Hylkää ja estä" "Poista" "Poista tili" + "Poista tiedosto" "Poista kysely" "Poista kaikki valinnat" "Poista käytöstä" @@ -304,6 +305,7 @@ Syy: %1$s." "Odota hetki…" "Haluatko varmasti lopettaa tämän kyselyn?" "Kysely: %1$s" + "Kysely" "Ääniä yhteensä: %1$s" "Tulokset näkyvät kyselyn päätyttyä" @@ -543,4 +545,6 @@ Haluatko varmasti jatkaa?" "Sinulla ei ole oikeutta lukea tätä viestiä" "Viestin salauksen purkaminen ei onnistu" "Tämä viesti estettiin, koska et ole vahvistanut laitettasi tai koska lähettäjän on vahvistettava diigitaalinen identiteettisi." + "Laitteesi on liian vanha, tarvitset laitteen, jossa on Android 8 tai uudempi." + "Karttojen renderöintiä ei tueta" 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 caca4b5014..37f1547a1c 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -18,6 +18,7 @@ "Információ" "Csatlakozás a híváshoz" "Ugrás az aljára" + "Ugrás az olvasatlanhoz" "Térkép áthelyezése a jelenlegi helyre" "Csak megemlítések" "Némítva" @@ -98,6 +99,7 @@ "Elutasítás és letiltás" "Törlés" "Fiók törlése" + "Fájl törlése" "Szavazás törlése" "Kijelölés megszüntetése" "Letiltás" @@ -303,6 +305,7 @@ Ok: %1$s." "Kis türelmet…" "Biztos, hogy befejezi ezt a szavazást?" "Szavazás: %1$s" + "Szavazás" "Összes szavazat: %1$s" "Az eredmények a szavazás befejezése után jelennek meg" @@ -541,4 +544,6 @@ Biztos, hogy folytatja?" "Nincs hozzáférése ehhez az üzenethez" "Nem sikerült visszafejteni az üzenetet" "Ez az üzenet azért lett blokkolva, mert vagy nem ellenőrizte az eszközt, vagy a feladónak ellenőriznie kell az Ön digitális személyazonosságát." + "Az Ön eszköze túl régi, Android 8 vagy újabb verziójú eszközre van szükség." + "A térképek megjelenítése nem támogatott" diff --git a/libraries/ui-strings/src/main/res/values-ja/translations.xml b/libraries/ui-strings/src/main/res/values-ja/translations.xml index ab5f64fc9e..a9bdedc1c1 100644 --- a/libraries/ui-strings/src/main/res/values-ja/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ja/translations.xml @@ -97,6 +97,7 @@ "拒否してブロック" "削除" "アカウントを削除" + "ファイルを削除" "投票を削除" "全ての選択を解除" "無効化" @@ -292,7 +293,7 @@ "または" "他のオプション" "パスワード" - "人々" + "ユーザー" "固定リンク" "権限" "ピン留め" @@ -300,6 +301,7 @@ "お待ちください…" "本当に投票を終了しますか?" "投票: %1$s" + "投票" "総投票数:%1$s" "結果は投票終了後に表示されます" diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 303e8ba929..6336a8de92 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -8,13 +8,16 @@ "%1$d자리 입력됨" + "소요 시간: %1$s" "아바타 편집" "전체 주소는 다음과 같습니다 %1$s" "암호화 세부 정보" "메시지 텍스트 필드 확장" "비밀번호 숨기기" + "정보" "통화 참가" "맨 아래로 이동" + "읽지 않은 항목으로 이동" "지도를 내 위치로 이동" "멘션만" "음소거함" @@ -31,6 +34,7 @@ "재생 속도" "투표" "종료된 투표" + "위치: %1$s" "QR 코드" "%1$s에 반응하세요" "다른 이모지로 반응하세요" @@ -45,10 +49,15 @@ "방 아바타" "파일 보내기" "발신자 위치" + "%1$s님이 %2$s에 보냈습니다." "제한 시간 내 인증이 필요합니다.1분 안에 확인해 주세요." + "설정,조치 필요" "비밀번호 표시" "통화 시작" + "영상 통화 시작" "음성 통화 시작" + "%1$s 내 스레드" + "%1$s 내 스레드 목록" "묘비 방" "사용자 아바타" "사용자 메뉴" @@ -66,6 +75,7 @@ "통화" "취소" "현재 취소" + "파일 선택" "사진 선택" "지우기" "닫기" @@ -85,12 +95,16 @@ "계정 비활성화" "거절" "거부 및 차단" + "삭제" + "계정 삭제" + "파일 삭제" "투표 삭제" "전체 선택 해제" "비활성화" "취소" "닫기" "완료" + "다운로드" "편집" "캡션 편집" "투표 수정" @@ -197,7 +211,9 @@ "베타" "차단한 사용자" "버블" + "통화 거절" "통화 시작" + "통화를 거절하셨습니다" "채팅 백업" "클립보드에 복사됨" "저작권" @@ -285,6 +301,7 @@ "기다려 주세요…" "정말로 이 투표를 종료하시겠어요?" "투표: %1$s" + "투표" "총 투표수: %1$s" "결과는 투표가 끝난 이후에 공개됨" @@ -448,6 +465,9 @@ "이런, 오류가 발생했어요" "🔐️ %1$s에 참여하기" "%1$s에서 대화해요: %2$s" + "실시간 위치 공유" + "위치 공유 중" + "%1$s님의 실시간 위치" "%1$s Android" "강하게 흔들어서 오류 보고하기" "스크린샷" @@ -455,6 +475,12 @@ "옵션" "%1$s 제거" "설정" + "위치를 공유 중인 사용자가 없습니다." + "실시간 위치 공유 중" + + "%1$d명" + + "지도 보기" "미디어 선택에 실패했습니다. 다시 시도해 주세요." "메시지를 누르고 \"%1$s\" 를 선택하여 여기에 포함합니다." "중요한 메시지를 고정하여 쉽게 찾을 수 있도록 합니다" @@ -478,6 +504,7 @@ "메시지 %1$s" "펼치기" "줄이다" + "실시간 위치 공유 중" "이 방을 이미 보고 있습니다!" "%2$s 의 %1$s" "%1$s 고정된 메시지" 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 baa2eb8506..2b34e82f91 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -101,6 +101,7 @@ "Odrzuć i zablokuj" "Usuń" "Usuń konto" + "Usuń plik" "Usuń ankietę" "Odznacz wszystko" "Wyłącz" @@ -555,4 +556,6 @@ Czy na pewno chcesz kontynuować?" "Nie masz uprawnień do tej wiadomości" "Nie można odszyfrować wiadomości" "Ta wiadomość została zablokowana, ponieważ urządzenie nie zostało zweryfikowane lub nadawca musi zweryfikować Twoją tożsamość." + "Twoje urządzenie jest przestarzałe, wymagany system Android 8 lub nowszy." + "Renderowanie map nie jest obsługiwane" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 2acc9df99f..83a93763d1 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -532,4 +532,6 @@ "无权访问此消息" "无法解密消息" "此消息已被阻止,因为你未验证你的设备,或发送者需要验证你的数字身份。" + "你的设备过旧,需要 Android 8 或更高版本的设备。" + "地图渲染不受支持。" diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png index efd83f7af8..3419248cbe 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2e4ee3ec519388e7acdedac96636aa3f0e0d9dc4f4a061a0b3e4c345b824a3d -size 24961 +oid sha256:9af82b0e785a85acb47d0b02b682c659bc13dbff01eb969f367d2ee413b6cbb1 +size 25165 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png index b7a581021f..706b96c796 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d367fc2d1c620eb370e28db890e4f01acd85b0273b241d0b0b701f23939fabc8 -size 25377 +oid sha256:37dc07b7be3f81a8b3925e1325baecff04dc2f3cd92316140f969610b6362169 +size 25691 diff --git a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png index 0c1e7996ff..b8c5829e1e 100644 --- a/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png +++ b/screenshots/de/features.home.impl.roomlist_RoomListContextMenu_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6175b377fcdb3f70739c36ceb1d59a7e108d8f1d08b4c10ec6d28744ff0f0545 -size 27442 +oid sha256:ace88630fc2d12ce8f9f610683626ff9d8ad8d3a18564791d3abdcff5743ed46 +size 27731 diff --git a/screenshots/de/features.home.impl_HomeView_Day_6_de.png b/screenshots/de/features.home.impl_HomeView_Day_6_de.png index 467350fe56..10abd6aef3 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_6_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cc8182e0e5413cc51f0df6d2724efae8af4101f9ac6cf21ca9260c2a2aa245a -size 57989 +oid sha256:a50cbaec05c9547c992342aba6f5042bbd0dcb6cae0a599e91b26edbafb59ac0 +size 58277 diff --git a/screenshots/de/features.home.impl_HomeView_Day_7_de.png b/screenshots/de/features.home.impl_HomeView_Day_7_de.png index 774818bd57..2e95e34feb 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_7_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f235995960c6237f4e03e7b0736cb280e1627590b0761a334c7a969491a12da8 -size 57321 +oid sha256:b53cae15b14893fce996dc087b4907cb21104e220d143e64192753d1088c0c44 +size 57599 diff --git a/screenshots/de/features.home.impl_HomeView_Day_8_de.png b/screenshots/de/features.home.impl_HomeView_Day_8_de.png index e644082cae..5f62cc2ebf 100644 --- a/screenshots/de/features.home.impl_HomeView_Day_8_de.png +++ b/screenshots/de/features.home.impl_HomeView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba1fbfc866d6de2cafae55b6da0721f12a1acb25163ad31ae2995f6ff9016856 -size 56039 +oid sha256:d88ef28257f6c0893e8bfff2d98e5aff52f0534539e91fe6ca490989548a65b9 +size 56346 diff --git a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png index 7ea7736a31..fbcb03812f 100644 --- a/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png +++ b/screenshots/de/features.linknewdevice.impl.screens.error_ErrorView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06fa1d9cfcdd7e81dd5d08f584651d982136e7491041b4db6c0feb9875c2db49 -size 43509 +oid sha256:026cde07bd625e7457af44ef19c4865f6bad8fc7c8ddfcc43c7a6dbdd4acf467 +size 33651 diff --git a/screenshots/de/features.location.api_RenderingMapsNotSupportedDialog_Day_0_de.png b/screenshots/de/features.location.api_RenderingMapsNotSupportedDialog_Day_0_de.png new file mode 100644 index 0000000000..06c4be2c81 --- /dev/null +++ b/screenshots/de/features.location.api_RenderingMapsNotSupportedDialog_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3065603f9b067f0ddc89a7b8dc24921afd031f4658f6ac11de8feb0c4bf26b62 +size 23910 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_6_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_6_de.png index d52980f113..0c68797fef 100644 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_6_de.png +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e8cb1edecba35913251d57d6eaa9d48149f7b2a26c180978c0ae34d3d354d97 -size 19693 +oid sha256:6fe56f20ce5792f9dd60bf0ae08fa5c5c78fbb7a5f1a80eb2fe973716be69c00 +size 19831 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_7_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_7_de.png index 0c68797fef..a49023fb61 100644 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_7_de.png +++ b/screenshots/de/features.location.impl.show_ShowLocationView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fe56f20ce5792f9dd60bf0ae08fa5c5c78fbb7a5f1a80eb2fe973716be69c00 -size 19831 +oid sha256:fa435ee5d684b82c66c7207e0bcdc2d608a634e02d534530cccf8abd0f40f784 +size 21292 diff --git a/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png b/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png deleted file mode 100644 index a49023fb61..0000000000 --- a/screenshots/de/features.location.impl.show_ShowLocationView_Day_8_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fa435ee5d684b82c66c7207e0bcdc2d608a634e02d534530cccf8abd0f40f784 -size 21292 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png index 8df559390e..2ce79cc887 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5758b86295a5cd92cd103992ee601c762b876fec28a4380c793a6c054993513b -size 35016 +oid sha256:1ee8832fede93fcbfccc7e554a7528ae77a50a3a8f8d9ed87e4bff209eb62807 +size 35457 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png index 044901ea5b..d8f4384537 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ce01cf6c3d326ad4208fa9832cab3e56164f791f59833b1f73d6f29e531dab8 -size 54709 +oid sha256:9b8b6dd8ce354e875e682b0f228bd9d4fc90b561202675aebd8f9bf83b228517 +size 55096 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png index a7ba678616..612c0d3171 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c52f77e77180d8e15b67abefac41b54107397fd489440d5aafdc4079d0100b8 -size 55075 +oid sha256:e9cf37d1b417d0626271087a6e1fa573ebd638192bc369dd376c67c67edcfdcf +size 55463 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png index 220ebf1ac9..90cddf85c4 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29ac70d223cc1f1c3a63c21431864f60bd6e199de574e1a2cded10bcaac11a8d -size 46842 +oid sha256:02f0679f2d5ffc1dc754f9bb317f765c3c49ee1e190d0aa234637c9a28ff8cb0 +size 47351 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png index 5150bb2206..c221f764c5 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f04c852c210a027d1792d7512aa9ecc3a6913d3ba9d18d8680df991f8050f57d -size 51086 +oid sha256:f753605522c268a0835a279f0680654bceb86f38ea4b76605fcb71917a77c7db +size 51594 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png index 3c5ff4c7c5..6a18dd3f5f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0d6742849583dc21500d5dba09bf535f71a5bcd070cd129305c1dffd817d8b5 -size 49443 +oid sha256:820d30e4e8ff9329dee9d49fa6f0c28c5b1f5e4684db085c8e6ceb265fcbfb88 +size 49997 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png index ef8273806a..431e2101dd 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fbf746337b88e848b7e99e526efe2ba468d28fd1536970ddc4e02e0d946e026d -size 45151 +oid sha256:a403f77984cca9eb94755dd74f1bd6db854725347dc8f15a207b20cc2dd43ee6 +size 45253 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png index b75f0d76b6..36a305a42d 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63b5fdd8b50485c835ce04e61b3c21d2f6d306d73c98675d1368772f4ca74c1d -size 49708 +oid sha256:17c1da5810c4d8ae13670fac57d5cb57b204cfbed83e71b2bcfe5657e6bf354a +size 50247 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png index 0d842f530c..c40acb066f 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d7e27bf9c1e2fa0bd86fbf986ced9f21ceabddae14c00086b691f182aa7ec5b -size 45918 +oid sha256:0043a95fd7946c8247a963e38057ad8b4f62f4112ede1df7cf6e609167dd1e87 +size 45974 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png index b6587618ec..0cb58754ac 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f61af5729e24ee0b15af8df2bd641411ac8f17a5b7f9a76899c6d05f328dbd05 -size 48879 +oid sha256:cccbcf0324e642f49c739f079bdf7ac18876f4dbb99e23d504b1c22669a376d2 +size 49352 diff --git a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png index e41b40ead2..37b41517cb 100644 --- a/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png +++ b/screenshots/de/features.messages.impl.actionlist_ActionListViewContent_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f83a10070d130542d0783c8a39f588b869ec265b9c0026afaeccc0b7b50602e4 -size 38159 +oid sha256:a82d583db67f7507f0ca837b9b75897047808a1a7f3d828c7213a9a6f4578429 +size 38613 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png index 6c398bfae2..210020e06e 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56f7a86707c00d1833ebbb26182ad9f9f010ed8f016713076232d4134c8bb6e7 -size 329737 +oid sha256:99d5cf65c968619f2a99f382e2de07d08022f0325349f86571c5766d17fff9e0 +size 327894 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png index 25ae9b6949..3ee21f4b12 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6907cdde67cfb41246b9b86af19d0d46bf92914b21f9ab68d522f07dd83af153 -size 284058 +oid sha256:82b816075c7ca6898da2c295740f6828a15a16062e944f01a4401ac0b84f6008 +size 282325 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png index 0d11558b0e..7aeafb4481 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ec83baa6376355fcfa1e9a088faee47f5d7d87ed7608bcbe35e7438f9e0cdea -size 257324 +oid sha256:11222c39d8ef830649fdd0b35c95b31b4d934c0c2667674ef7d659d9a3320b52 +size 255483 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png index 6fadc05d09..2c62689a45 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17bd7ae449ad1fb92c75e09848bddf907d15cfcbbcb9554350893c7a6019add5 -size 278513 +oid sha256:bc3d7320da4451c862f1083981d628a4df4115e95f9e88db7e0d1910f9890eba +size 276670 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png index 3eba67d146..1ba1a3158a 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e692a9e204db51af62fa83af8c1c7dda4fdfe81077d3abced093f3f8dfdef2df -size 252607 +oid sha256:b23a3fd19bb6057dc13f61525a4589a3d1dd66a97818c4229d4db35cb6f56faa +size 250779 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png index cb1f4a9175..3f3fa22933 100644 --- a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e881c0e26edfd9f4f2eb7325e307edcd4220aae42337ce8d238cd64c88722de4 -size 312002 +oid sha256:028b3249d8d63cdf799ac1e0ab530e4a46140c53adf8b83a05153d03d030d4e6 +size 310168 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_de.png new file mode 100644 index 0000000000..aaedf662af --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24b6a36a3efa0127a099081ef711fa804879ddca347849580e80c2a147c7f4c9 +size 282515 diff --git a/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_de.png b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_de.png new file mode 100644 index 0000000000..5b2ff9626b --- /dev/null +++ b/screenshots/de/features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7138b55da8a0a0a3ed2c049ec84d01fa298223a3b6ffe391fad7a7e3a3a3beb2 +size 282778 diff --git a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png index 11e4bb1119..a10d3f58f7 100644 --- a/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png +++ b/screenshots/de/features.messages.impl.attachments.preview_AttachmentsPreviewView_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:130c46b4bd3589cc6a6e45a04f9793b742631daf21cbbf971f8e79a00f589ce8 -size 329628 +oid sha256:f350e78ea3bba4a80d22af4df58b8a6e415a68fe56770027c9da95da0cab0973 +size 327790 diff --git a/screenshots/de/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_de.png b/screenshots/de/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_de.png index 14abdc0caf..198bd4553b 100644 --- a/screenshots/de/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_de.png +++ b/screenshots/de/features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4f823bf8a422775693ba64ca10e087286967d11c8ed94a3198f108abd3a9367 -size 27026 +oid sha256:1ff21ba8a011e0aedbbcad919f46db1a26895ae6e286335055532f43dbf505de +size 27661 diff --git a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png index 807e16c40a..6d9b506ac7 100644 --- a/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png +++ b/screenshots/de/features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbdd0d31786b29709c02e9f35191af1334b6f6a32cc0614a833f79d47551fc70 -size 42687 +oid sha256:cc44a5bef33ae51161dee412b729baf328b188976156d7eeb7677039ad60b7cd +size 42147 diff --git a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_4_de.png b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_4_de.png index ba7d60941b..2cc385f926 100644 --- a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_4_de.png +++ b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e916a49d3cd4d81d8ec5f3d66f0b930bfba6e9c20f1b9c5f7d4c0bfe51895036 -size 67398 +oid sha256:a6325d21da32e6def01f35803b4823d037076f0c96c24d864fad33b0a7d9a7b7 +size 65731 diff --git a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_6_de.png b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_6_de.png index 0d70a656c2..1d36e3c163 100644 --- a/screenshots/de/features.messages.impl.timeline_TimelineView_Day_6_de.png +++ b/screenshots/de/features.messages.impl.timeline_TimelineView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86fa2e5742ba38c173add0921615a18c1271caa783fd1a505c80a06096a3c248 -size 71646 +oid sha256:853058d4d90fc9d39115263ab74e7813ab6a52844cc56cc4a30c3dcb42e291b9 +size 70016 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png index 8e73293bb2..5f24ba6d45 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1afe6a244e864befb72decf8d2518b9588b3f59b5382c8e5c6130a2ea33e23b -size 41952 +oid sha256:c100900785c0c11cd69b60da9ed3909f135b876d0546722f707febd24e796256 +size 42510 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index 9dfcbcf1f6..e3f488bde5 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce3c8e771e43c186cadcf495a7d85d6ad36c089373782c209170282e0e5fb1fb -size 49709 +oid sha256:4d7c45ac3f5fd12f909706a1da3aae7620472958966eac0d49ba3020dc52b312 +size 46054 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index 26411218a7..1022a785ff 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db7a53d858a08f1396a9c04b6a253623fa25160f90de62be9dc187f72fccedbd -size 48374 +oid sha256:41931967f6bc1b5a1244764bf1c9b8bf498f107edf8d85f6fd07ab3d6c5f2468 +size 44146 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index db935d5dc9..3a8ba95641 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38e49c07c2f49bee8ba4d37f684be34ccb247bfecbfea5b1733e76246d160a45 -size 47086 +oid sha256:98e3c0f5bd719b4e5c47852e8e5620fa79724cd48d9360c4f2064f8a650555d7 +size 43499 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index d0343f2f12..00ce182ff6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f91bfd31c40b1cea5b4dd763dbc92eaaf7335d078ac496b51b05617c01f8b96 -size 48934 +oid sha256:e6da9e5dcfe52ebd26e4b590eb7cedb751a53729344556384d04d66da6f8b9e0 +size 44489 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index 12ccf2ffc3..f09ef8bd09 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d85a9aa1456dccaef8a7cb2c8b8c756e54c4fe03402d39d1240624d279449c28 -size 48821 +oid sha256:c7db4c8739f40a186a84c8420dbbf44c53aa1a079c74437348d651eb91482e8c +size 44445 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png index 625ef06b29..3156bdaf97 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b65c937240f2a5bee4e4170a511fff5437c5bce47fb987674548487fcf72fd1 -size 49350 +oid sha256:3626d055f83fcfe23d0cbd79b2be99fb2238ce4e5da4c80b17bc20353849c32a +size 45859 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png index 0ab84d817e..cd433d98e8 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f965ec5c45372e04fdcdc1c3c65633551f83e8dede75df8e8c329f849f487bf -size 49885 +oid sha256:8e1a5175e48e200145a132a9b87cdde64ff9c5bcd14e049c7ce81a27e11c9661 +size 46222 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png index 4798c4bc07..368a0713fa 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b68d23accbe81e6913d16ef6bb15712105422911905f274d66ef656f98d8a28f -size 49201 +oid sha256:1d192d1d08b4bb235e894141d79d7fe0a19e5fc7d1dfffc25d9b7b797e9f5a1b +size 44681 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png index 11a68a7982..786c66da58 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3699ee438dbbd4aa0e5c5cd5a054304888421cb8c8a822dc52cb8369816f7feb -size 48429 +oid sha256:e30a3b6f61cfa1933989eadac016b2dbb565634f95bfb118c3a0c248bbbda9c3 +size 44129 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png index de0323860c..7d0b7034cd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3eada7d271b1f649d931e5077fc04e232046fd4171881c673bab8eaf990f92b -size 44452 +oid sha256:91351802609eb6468d1f544a403246d88c38a58e76437ddeda36d1530499475e +size 42646 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png index 65d67f1746..3bf58a45d8 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:934be11ba08bc83461c1d859b3cf8b6f367feb89f6a779689ece209c30d8eaa8 -size 44441 +oid sha256:24238416160eb851769d2f6634e267dba824450e93fd7f193d5d6e3806741afc +size 42641 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index 637fe31e2f..67d3aac6fe 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:540a461ec0a84d22269abc644dd62444223818e337edbab2f79317f80cc24696 -size 44107 +oid sha256:6ccca13029b6f5fdbd79bd7cdacc90bfc4e4acea0ab59416785cd1ed84f0e110 +size 40637 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png index bfa129700d..57782baea0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30918ebc116ebf2058aa619debfa9165d5ef95feb916a83e94e49d09377435e8 -size 49193 +oid sha256:df391568836eb548b3226c88ef9bfc60cf62ef8bca9eb20dde68c18c06ff9e3c +size 44645 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png index 8a5276b209..93bb4e5916 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cf3437703035eec0dd9623032bb62f1472b1a5fe996b7b85b9050385eb118ba -size 49109 +oid sha256:615c16c1d98590e0d4cc81222a1f8fdc534681f5ad99ce3cda82bc4f024d555c +size 44587 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png index a9e5ff3132..b7ec17f054 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:983d019c59d97d4bbe5b42562d6d028bb094a5b405fb2adc033232838952890d -size 48673 +oid sha256:abf088b6f30665a9e5d9d8a60ed170d0b8b7c219e8b71558ac9900986de1ef54 +size 44251 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index 404ba5c5c1..4db528159c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c79eb29314fbb2378034b03d368d5ca49ae49c6a53387784445a0b97e3cc56e -size 43324 +oid sha256:70bf1adc246f1d2b792048ea5739358c39935daab42b9a061241dffac4f8501e +size 38503 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index b492032c83..c645f66027 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d605c8379ff6844f7581231574cdfb50e5bce0b2e33c886fd43b520576f9149a -size 45963 +oid sha256:42392b475d4ef7351a0f91d2803beaff53c99dd2e3c682e6619c8edac638899f +size 34399 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index 09c0f584a7..ad9a59b471 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:355905f6a0fc25abe31db1359f24af1acb60bb3a013e697663c3ee9f87a9be52 -size 47386 +oid sha256:15f8f4091caeda71cc1a3cda9c100a0b5aab7bfa6feff4e70e6f2308258248b4 +size 42985 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index 0fd7d3e667..49d2def3f5 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a378c9b3a9bde80050444029ec6eaaaf76a5182c45babc898dbea24207466a32 -size 44054 +oid sha256:2f0c96227163246012a54fd985b282386231ffb9b03952ad4c2058500a081cb4 +size 42356 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index 6392ad39ea..b8f184af2c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:737de18454bbb78bd9ca789ba1ab821f203c54d55301a5851ae01e336cc42260 -size 45324 +oid sha256:9406cb785c88cc1b370678140b57b5928e46616274529853e7700ad48768feed +size 34945 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index 425e08b224..e9a027a9d6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2ce5bb8422a493541b30fe3eb95f756367358e039b759967e1f4d96f7cf337a -size 49729 +oid sha256:68ae4856b0ea5e73705d83b927577457695b8e5908c7d71e029953ee9cd8d8a7 +size 45124 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index 6be8a7cc91..5911a811cd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4fd1c7495908336646a05655f1c4c2d2ea0ece1e690f69164cc2946273a077c -size 49038 +oid sha256:4883dfc428a83e97ed406b172c91772ec7b51c9df074b385d7d719abd2ac1f1c +size 44648 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index 8927152505..cb17901678 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12d25ae598f1a05aa86f0125b6acbaa4137becc2900a2ac649880c521ba2ef52 -size 48034 +oid sha256:2e0782f2c450127e1079999e76c4683c0550e1c3fc6867fe6a8438c457a1c212 +size 43210 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index 86a072cb2b..1ef1806d32 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7382605cb34650919a0b9f1d1e9460e28ce963dc4ff899fc6906dfcdd73dda50 -size 50783 +oid sha256:ca9bd8eaae03a23e19f307e48200a3acad5adaf25f85b9ff996dfd34e2afa7ea +size 47683 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index b3d5dd204c..09d0b0b2dc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6f057a687620b49fde08e0c241aadca011d87154e73b7cbaf21a22896de07c9 -size 49348 +oid sha256:8ce03c0a8cd57000abb832adfc9109ce11901f2e7299ede7f0d1d26954d71dc0 +size 45588 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index abf7a28899..8c96b6425f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a08b46085a213f7c6a93f24c2afc04cab4a0c30369d6eaf552cc6e58f50aea34 -size 48160 +oid sha256:14e924fdb76d565050a354a7fbf423e2f2a42bf4ad97e4ea936ab774fd4c7817 +size 45027 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index 257169012b..4bc445b2fa 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e624adcc59354fc21c960547d0766929b95e1a2745b667eb57dd5e62d3ca8451 -size 49914 +oid sha256:b65295a9ef7403a39960379d005702837cdd994f95ef4ac68901052c28d3ea1f +size 45989 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index 3804c84919..f47c6ad939 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60cc99a113658fc4222b68b4801066c112d02dbd5c5b39e12471f77bb71ab348 -size 49807 +oid sha256:29964bd165474c6d04de2030c5529af2e2413ee84f3aaf9bd317303a829fcd46 +size 45922 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png index 080e2d2199..c52350d443 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd16c5e9846823589ad5b22568c25b0cf3335cba8b467ceb3c9186e5b84e7fc7 -size 50318 +oid sha256:9ecfbf047383b98642fadba05e9ce0fcf74126088ee690bb4c32be65588408ab +size 47473 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png index f80834a2e0..61cde4f5b1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d3dbdcfe1dedaabb5d03dbdcc2a2f377fb6d1aafccd6b70cb147a97bf23b1c5 -size 50929 +oid sha256:9ce8737bb8f4774165acf35250efcf9ca6f05158073ffe50601ffca66bcc60c7 +size 47907 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png index b43767563e..d3a81f277b 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0761670005dd4463b662efb563a5270f89790470017d42001d00ce44e96f045c -size 50201 +oid sha256:592d316544feaafacec23fdcf5cdb4470a8207835ba87a723ef45b4b2f764917 +size 46236 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png index 3c0a33e5fc..dc25cda13d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6361321527d1bfa1ea84b361dec64c3746a85079a4048666e4016f52d3d34246 -size 49681 +oid sha256:88816d1cd342284e6af3da0e89236a03fdc149ebe7d621d8590d8730d8c81bcc +size 45758 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png index c3b0b28c5d..805a1f7fc5 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5bbd36791567c89996dcd755b3ff4f956f69f1ff3f6f66595f16e1e733fe0a7 -size 45751 +oid sha256:ce7df8bc9256fad4cb3cff8f3147f1c74ac31a9e379b86c428b31d8ee0a1b3ff +size 44438 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png index 7dc11fb06d..94ba841c62 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_19_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bf0969b8a24c8d06fb3253646d6e2d0a94572cc346a8a9f9522ae48e5b4217a -size 45686 +oid sha256:df68b3579de58586a02141996672bb25cdf1056030a2341ae70383fbe2648f04 +size 44404 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index 4fb1d5bebc..c43dfc4765 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cfbd204c871feea36d0ac5ec740049488a9d3511156cf7a3e30d724d62fbdae -size 45187 +oid sha256:f036fb2f808035db5e5891f48c51586883f1cf52116c07be1698cb901bb12a28 +size 41932 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png index 96b2a06800..11bc2637cd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_20_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84af752733042d4d487a934144525bbcf31cf9c1cb1454a5561ea9b3bf794afd -size 50205 +oid sha256:4fa2e5f4110ac0235e6e15ce5dd11b684a301e8508ef4a339f819239ed7bdf6a +size 46219 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png index 3576b6b30c..cadcd1e2b4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_21_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a5c142c8539e0a7d40f4455e96d3d5fe1003f2cef84095709f15fd63b945a06 -size 50130 +oid sha256:a8f1c358b1de30937278423b42bdc468cd36a82ff78cb0c687cd26c9e8ce3b05 +size 46106 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png index 6905512be4..9ed59d7c04 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_22_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:375274f827cb0b1a56461a95ebbbc12ab8dead23a41059b0909d87892b5b7f74 -size 49645 +oid sha256:3e4d19c67a12095c513363905af5f9d01b1f96ed21f8809b3a34162fb7f4cd18 +size 45707 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index 23620c662b..784d22cc5e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9737debc2f342c5ce2b20f48151597e164526297d2789b04bfda74e4cc70153c -size 44357 +oid sha256:dc77c07d95d2fe8482d0aa1a16f02ad1f1bb12b42b96e49613c305bb7001d85b +size 39970 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index aca9ef2193..4e841931bb 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d16e1a72ac9f47e0236b5ce3bf82c7e8d0d3ca0b245aba5fc751a0b9bed6cae -size 46946 +oid sha256:2f92411b4c22dd91cea3fe2b83e68855106f0cc8f8e98bff7120d7ad9ee08b8f +size 35510 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index ab1ea77140..b81166bff6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e483482b88e7edc6724459da394f87a587ada9ddefea5d05d9bdcfe57e34ec47 -size 48423 +oid sha256:dc47591d0c528d2690c93cc753944a3d14cf5874a76375e85280f20248e0a461 +size 44643 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index bd270b4ed0..1ca8f85e28 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c8130c023137a1c3872202df289f364a3cc85217c7b0d7d36609d1fe896e96c -size 45304 +oid sha256:6b8c3af45a0302fbbb8162acb3cff65f3ece5d9c583655c73490ebfc4d67fbf6 +size 44110 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index 26e23487c0..b7df110e3c 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c67de39146bb8c0049de90223d4a7293e3f42ab8f3780cc0980e62c97d10760 -size 46651 +oid sha256:2e2ae44ab89b544cb00c4cc72bc20eb4b7c1ad8964bf6eaf95499cd4727ecb01 +size 36461 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index 2001c831b0..c3363c0921 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69695044a9afdc82d1a65e0911effcbb660dd550c31d5b8ae1f6a0f674dacd30 -size 50843 +oid sha256:b626694fb5e69a36f345ff1500aa502fcc6b780d94af51670a43891702bbb0bf +size 46728 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index bbec3a797e..1bda15b8d4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:acbbccc700060f3511bdc2a44661ecd1442bb8db107c52d8a3ea93085e3cc370 -size 50181 +oid sha256:e4d3dc8b6a30ec8a716af574f5f92d33cd1804eeb33427b162a439257157e694 +size 46229 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index f1098ffed9..8764bb944e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:891f50447359e8697ca26ac27179fb1687851c24156d36006764c874592c7584 -size 49072 +oid sha256:2dd181d7a3d507f25a7cb7717dd96a0a2e6f37896291ce160c356f8ab89b3637 +size 44769 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png index 5781478873..c69f23d822 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a78ad12392ea4d4e9e54ffbc786a6e3eec73294e088f5403db88af5269a6b003 -size 18523 +oid sha256:21e3fa2d81c7fb57c8c89581e6e6202406b25dd83c19e029fb46c0ed3ad3ac16 +size 18663 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png index 819491fece..c141aa0941 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dc117e18612891f202c6bc093e4a2063632ee974939606af6eb09ee36deba9e -size 22172 +oid sha256:07b7bffa03de36ddcf461b74d5750d92ea67728b94c864b3c8cb8a1aac48ae30 +size 22349 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png index ad63075b13..343c53d7c0 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc3534d406c3ef468e9af1b86d8d09998fd173aa1f63627412053f4e4d76bbcd -size 27625 +oid sha256:d7b55e28962e18b4a14f4b1e57f455cb1183b993ddbc6449352222a0e338afdd +size 27748 diff --git a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png index d5df6b6df0..c631961b73 100644 --- a/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png +++ b/screenshots/de/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1d10f6ddd38cffdf7f8ba188790f6115b18b95de401ce08ba4530f249f99410 -size 28109 +oid sha256:5d3dfc118145e83a3ef346de399e4f07aaa24e3adbeb039a24387d9ba666beb7 +size 28210 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png index e7254790cf..9f12c8c18d 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7bf1f5e039bdff2ba1da2678d57637564288733b9958042b86867350277ca9f -size 25025 +oid sha256:0c80ae6573c4d41b14827623890cb6b211783c708d7417c3b9f1e1ab29462098 +size 25198 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png index c7bfa31dae..81b5bb4a95 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a73316a4090908c2c8db15fa408e132f3fc7308c898311008f59ff62fdb94801 -size 23050 +oid sha256:09404876c06fa55c2e01f48380f7a4767d8c55f0cd46bdec78f4d7687ba8101d +size 23150 diff --git a/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png b/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png index 4263d29577..012c3b1574 100644 --- a/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff83b8d841c355dcac18d1ba0c7a66d0927e23be66bfabe8bc791e6f2ff3db22 -size 14008 +oid sha256:fbebdd9012c183c27ed79a3cb1dd5b823f73025756d9875c0c5a7af6f126dfa1 +size 14153 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png index 5fa5394165..748733038c 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e2325778fc5bdaa5ffd421f77cb83d1927ad4ad6731337b4878a99d3fbfe8aa -size 46053 +oid sha256:f02798a686487a372f781ba8db1c08364b4b7364e49ca6084eb48889967a329f +size 46360 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png index 5fa5394165..8ad903b190 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e2325778fc5bdaa5ffd421f77cb83d1927ad4ad6731337b4878a99d3fbfe8aa -size 46053 +oid sha256:555a9ecff384c0771f9825e5d222c6094818fddf79f144e4061d933bf752fd89 +size 44528 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png index 1e6c6d88eb..3e11375bf3 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f845e0783b6f09ef7b0472f22465e8e5b086102d711e468f1466df45ff928ed -size 50410 +oid sha256:de1d4d817742b8390b7e09bd8c66ca1ce294b22dc2c8c111205bec3436b57222 +size 51558 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_de.png index 79f832dcc3..67b61583b1 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2917a353e78569a92d5d276e99ebfce334ad8848d488f58312abb9d7bcf654b -size 33327 +oid sha256:2bc723ee90bcd7038d78ccf738422daca8a0b0d54f9e86512399230912281d9a +size 33332 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png new file mode 100644 index 0000000000..508383d519 --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44d94152af559ac75475599fb5cf14defc96997e73e38600ff51c869903944f4 +size 45625 diff --git a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png index 1ec5f1ed66..5c0d5c66e5 100644 --- a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0321ed8731d4867c59c482a8e631291537556f6003659b9e0c2aa77d61883a69 -size 45973 +oid sha256:b64f60650f09cd069468a3860808bed51ae2d9e11f2dc0798965674a15e2e04e +size 46275 diff --git a/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_de.png b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_de.png new file mode 100644 index 0000000000..b45beedb7e --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0a4907f2ebf197b1cec1715ac6682d21b7b1d0a80eb5d05108f1971b648126f +size 12445 diff --git a/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_de.png b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_de.png new file mode 100644 index 0000000000..9541983cee --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e0d9c3194ce7ac6096be5007d9e4d3b8e3fc6784c0389b1d14a288b48677304 +size 13776 diff --git a/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_de.png b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_de.png new file mode 100644 index 0000000000..d1207e09a2 --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f42dca3f4cdeb760ea11c3edcb8751518e7e42a423ffbafaf12cc97d9cd4861 +size 14965 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_de.png index c4822fa433..b81a3c6a5c 100644 --- a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2c35cf93948bbc2c344bc678db1159e77769577054bee9ed168cd07ccea5de2 -size 28202 +oid sha256:73a6a48d04d86e9d5e9e546681863c32f651e6dc9af6d7f042e7e36b7fd98997 +size 28178 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_de.png new file mode 100644 index 0000000000..d8118a67f0 --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6298eeaf8442246d35fcca3b9e46755353d12bd9d6bfaa8a1e0447ee5d730e92 +size 198311 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_de.png new file mode 100644 index 0000000000..090a403022 --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ef79c13235d94887d873c42faa9b09c901efaeae7602336c81fcf7c62955ea5 +size 198916 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png index 16575bf2f5..25848ecda6 100644 --- a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5300a530f5afb27d55369a9fec9af46e4ceccfe3ae6926d7bff90a7b8cbaa02d -size 44430 +oid sha256:1a1cb205c91f54437af19cf47218a6a4b520c3d6763c36716d48a2902e9622b6 +size 44981 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_6_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_6_de.png new file mode 100644 index 0000000000..80d843ceda --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_6_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebf8c21f8967a2bce2ef74ac896497e6732f8bbab5c895bc3d33e760791ea570 +size 126353 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_7_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_7_de.png new file mode 100644 index 0000000000..a4c2824fe3 --- /dev/null +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_7_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b1e398fa1cbf25d16ba4da6b8fd0c09a96e4f10855e94e913a1e06f0cea9c05 +size 17008 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index edcd08b481..313d0ffc79 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -2,105 +2,107 @@ export const screenshots = [ ["en","en-dark","de",], ["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",0,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20602,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20609,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20602,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20602,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20602,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20602,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20602,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20602,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20602,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20602,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20602,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20602,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20602,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20609,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20609,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20609,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20609,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20609,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20609,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20609,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20609,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20609,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20609,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20609,], ["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,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20602,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20602,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20609,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20609,], ["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",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20609,], ["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",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20602,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20602,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20602,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20602,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20602,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20602,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20602,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20602,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20602,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20602,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20609,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20609,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20609,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20609,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20609,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20609,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_0_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_1_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_2_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_3_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_4_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_5_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_6_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_7_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewBlack_8_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20609,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20609,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20609,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20609,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20602,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20602,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20602,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20602,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20602,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20609,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20609,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20609,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20609,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20609,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20602,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20602,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20602,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20602,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20609,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20609,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20609,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20609,], ["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",20602,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20609,], ["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",20602,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20609,], ["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",20602,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20609,], ["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.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20605,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20605,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20605,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20605,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20605,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20605,], -["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20602,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20609,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en","",20612,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en","",20612,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20609,], ["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,], @@ -110,20 +112,20 @@ 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_AttachmentsPreviewView_0_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20602,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20605,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20609,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20609,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",0,], ["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",0,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20602,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20609,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -153,22 +155,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20602,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20609,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_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",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20602,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20602,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20609,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20609,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20602,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20602,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20602,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20602,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20602,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20609,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20609,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20609,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20609,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20609,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -179,141 +181,141 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20602,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20602,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20609,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20609,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20602,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20609,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_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",20602,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20602,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20602,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20602,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20602,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20602,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20609,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20609,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20609,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20609,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20609,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20609,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20602,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20602,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20602,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20609,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20609,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20609,], ["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",20602,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20602,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20602,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20602,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20602,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20609,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20609,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20609,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20609,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20609,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20602,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20609,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20602,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20602,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20602,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20602,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20602,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20602,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20602,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20602,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20609,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20609,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20609,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20609,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20609,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20609,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20609,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20609,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20602,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20609,], ["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",20602,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20602,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20602,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20602,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20602,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20602,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20602,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20609,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20609,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20609,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20609,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20609,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20609,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20609,], ["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,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20602,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20602,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20602,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20602,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20602,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20602,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20609,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20609,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20609,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20609,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20609,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20609,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20602,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20602,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20602,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20602,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20602,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20602,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20602,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20602,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20602,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20602,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20602,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20602,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20602,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20602,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20602,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20602,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20602,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20602,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20602,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20602,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20609,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20609,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20609,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20609,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20609,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20609,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20609,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20609,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20609,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20609,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20609,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20609,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20609,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20609,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20609,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20609,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20609,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20609,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20609,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20609,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20602,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20602,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20602,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20602,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20602,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20602,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20602,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20609,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20609,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20609,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20609,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20609,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20609,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20609,], ["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",20602,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20602,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20602,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20609,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20609,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20609,], ["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",20602,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20609,], ["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",20602,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20602,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20602,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20602,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20602,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20602,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20602,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20602,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20609,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20609,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20609,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20609,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20609,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20609,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20609,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20609,], ["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,], @@ -326,20 +328,20 @@ 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",20602,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20602,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20602,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20602,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20602,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20602,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20602,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20602,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20602,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20602,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20602,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20602,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20602,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20602,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20609,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20609,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20609,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20609,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20609,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20609,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20609,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20609,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20609,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20609,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20609,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20609,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20609,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20609,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -347,29 +349,29 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["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.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20602,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20602,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20609,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20609,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20602,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20602,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20602,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20602,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20602,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20602,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20602,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20602,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20602,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20609,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20609,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20609,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20609,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20609,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20609,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20609,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20609,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20609,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -389,49 +391,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_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",20602,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20602,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20602,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20609,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20609,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20609,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20602,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20602,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20609,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20609,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20602,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20609,], ["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,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20602,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20602,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20602,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20602,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20602,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20609,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20609,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20609,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20609,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20609,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20602,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20602,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20609,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20609,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20602,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20602,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20609,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20609,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20602,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20602,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20602,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20602,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20602,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20602,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20602,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20602,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20602,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20602,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20602,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20602,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20602,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20609,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20609,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20609,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20609,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20609,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20609,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20609,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20609,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20609,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20609,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20609,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20609,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20609,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",0,], ["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",0,], @@ -446,8 +448,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_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",20602,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20602,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20609,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20609,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["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,], @@ -455,119 +457,119 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20602,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20609,], ["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",20602,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20609,], ["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",20602,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20602,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20609,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20609,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20602,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20602,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20609,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20609,], ["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.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20602,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20602,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20602,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20609,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20609,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20609,], ["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20609,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20602,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20602,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20602,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20609,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20609,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20609,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20609,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20602,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20602,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20602,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20609,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20609,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20609,], ["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",20602,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20602,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20602,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20602,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20602,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20609,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20609,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20609,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20609,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20602,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20602,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20609,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20609,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20602,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20602,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20602,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20609,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20609,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20609,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20602,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20602,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20602,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20602,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20602,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20609,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20609,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20609,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20609,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20609,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20602,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20609,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -620,48 +622,48 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], -["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20602,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20609,], ["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",20602,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20609,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], ["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",0,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20602,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20602,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20602,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20609,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20609,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20609,], ["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",20602,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20602,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20602,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20602,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20602,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20602,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20602,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20602,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20602,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20602,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20602,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20602,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20602,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20602,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20602,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20602,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20602,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20602,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20602,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20602,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20602,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20602,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20602,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20602,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20602,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20602,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20602,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20609,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20609,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20609,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20609,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20609,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20609,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20609,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20609,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20609,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20609,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20609,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20609,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20609,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20609,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20609,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20609,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20609,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20609,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20609,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20609,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20609,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20609,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20609,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20609,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20609,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20609,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20609,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20602,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20602,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20602,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20602,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20609,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20609,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20609,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20609,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -674,26 +676,29 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_1_en","libraries.matrix.ui.components_MatrixUserRow_Night_1_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20602,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20602,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20602,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20602,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20602,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20602,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20602,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20602,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20609,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20609,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20609,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20609,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20609,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20609,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en",20612,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",20612,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en",20612,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en",20612,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20609,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20609,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], @@ -701,10 +706,10 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20602,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20609,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], @@ -714,20 +719,20 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",20612,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",20612,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20602,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20609,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], @@ -737,12 +742,12 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20602,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20609,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",0,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",20612,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",20612,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_9_en","",0,], ["libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en","",0,], @@ -751,7 +756,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",20602,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20609,], ["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_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -760,7 +765,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20602,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20609,], ["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,], @@ -769,24 +774,24 @@ export const screenshots = [ ["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_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20602,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20602,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20602,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20602,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20602,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20602,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20602,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20602,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20602,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20602,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20602,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20602,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20602,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20602,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20602,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20602,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20609,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20609,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20609,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20609,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20609,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20609,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20609,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20609,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20609,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20609,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20609,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20609,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20609,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20609,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20609,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20609,], ["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",20602,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20609,], ["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",0,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], @@ -797,124 +802,124 @@ export const screenshots = [ ["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,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20605,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20602,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20602,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20602,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20609,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20609,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20609,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20602,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20602,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20609,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20609,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20602,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20609,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20602,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20602,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20609,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20609,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20602,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20602,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20602,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20602,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20602,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20602,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20609,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20609,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20609,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20609,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20609,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20609,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_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",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20602,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20602,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20609,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20609,], ["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",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20602,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20602,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20602,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20602,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20602,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20602,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20609,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20609,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20609,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20609,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20609,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20609,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20602,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20602,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20602,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20602,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20602,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20609,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20609,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20609,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20609,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20609,], ["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",20602,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20602,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20602,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20602,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20602,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20602,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20602,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20602,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20602,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20602,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20602,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20609,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20609,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20609,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20609,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20609,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20609,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20609,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20609,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20609,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20609,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20609,], ["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,], @@ -928,225 +933,226 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20602,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20602,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20609,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20609,], ["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","",20602,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20602,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20609,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20609,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20602,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20602,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20602,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20602,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20602,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20602,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20602,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20602,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20602,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20602,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20602,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20602,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20602,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20602,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20602,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20602,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20609,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20609,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20609,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20609,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20609,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20609,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20609,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20609,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20609,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20609,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20609,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20609,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20609,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20609,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20609,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20609,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20602,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20602,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20609,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20609,], ["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",20602,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20602,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20602,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20602,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20602,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20602,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20602,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20609,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20609,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20609,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20609,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20609,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20609,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20609,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], +["features.location.api_RenderingMapsNotSupportedDialog_Day_0_en","features.location.api_RenderingMapsNotSupportedDialog_Night_0_en",20612,], ["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",20602,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20602,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20602,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20602,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20602,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20602,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20602,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20602,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20602,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20602,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20602,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20602,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20602,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20602,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20602,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20602,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20602,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20609,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20609,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20609,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20609,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20609,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20609,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20609,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20609,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20609,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20609,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20609,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20609,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20609,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20609,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20609,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20609,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20609,], ["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",20602,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20602,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20602,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20602,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20602,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20609,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20609,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20609,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20609,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20609,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["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",20602,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20602,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20609,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20609,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20602,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20602,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20602,], -["features.roomdetails.impl_RoomDetails_0_en","",20602,], -["features.roomdetails.impl_RoomDetails_10_en","",20602,], -["features.roomdetails.impl_RoomDetails_11_en","",20602,], -["features.roomdetails.impl_RoomDetails_12_en","",20602,], -["features.roomdetails.impl_RoomDetails_13_en","",20602,], -["features.roomdetails.impl_RoomDetails_14_en","",20602,], -["features.roomdetails.impl_RoomDetails_15_en","",20602,], -["features.roomdetails.impl_RoomDetails_16_en","",20602,], -["features.roomdetails.impl_RoomDetails_17_en","",20602,], -["features.roomdetails.impl_RoomDetails_18_en","",20602,], -["features.roomdetails.impl_RoomDetails_19_en","",20602,], -["features.roomdetails.impl_RoomDetails_1_en","",20602,], -["features.roomdetails.impl_RoomDetails_20_en","",20602,], -["features.roomdetails.impl_RoomDetails_21_en","",20602,], -["features.roomdetails.impl_RoomDetails_22_en","",20602,], -["features.roomdetails.impl_RoomDetails_2_en","",20602,], -["features.roomdetails.impl_RoomDetails_3_en","",20602,], -["features.roomdetails.impl_RoomDetails_4_en","",20602,], -["features.roomdetails.impl_RoomDetails_5_en","",20602,], -["features.roomdetails.impl_RoomDetails_6_en","",20602,], -["features.roomdetails.impl_RoomDetails_7_en","",20602,], -["features.roomdetails.impl_RoomDetails_8_en","",20602,], -["features.roomdetails.impl_RoomDetails_9_en","",20602,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20602,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20602,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20602,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20602,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20602,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20602,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20602,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20602,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20602,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20609,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20609,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20609,], +["features.roomdetails.impl_RoomDetails_0_en","",20609,], +["features.roomdetails.impl_RoomDetails_10_en","",20609,], +["features.roomdetails.impl_RoomDetails_11_en","",20609,], +["features.roomdetails.impl_RoomDetails_12_en","",20609,], +["features.roomdetails.impl_RoomDetails_13_en","",20609,], +["features.roomdetails.impl_RoomDetails_14_en","",20609,], +["features.roomdetails.impl_RoomDetails_15_en","",20609,], +["features.roomdetails.impl_RoomDetails_16_en","",20609,], +["features.roomdetails.impl_RoomDetails_17_en","",20609,], +["features.roomdetails.impl_RoomDetails_18_en","",20609,], +["features.roomdetails.impl_RoomDetails_19_en","",20609,], +["features.roomdetails.impl_RoomDetails_1_en","",20609,], +["features.roomdetails.impl_RoomDetails_20_en","",20609,], +["features.roomdetails.impl_RoomDetails_21_en","",20609,], +["features.roomdetails.impl_RoomDetails_22_en","",20609,], +["features.roomdetails.impl_RoomDetails_2_en","",20609,], +["features.roomdetails.impl_RoomDetails_3_en","",20609,], +["features.roomdetails.impl_RoomDetails_4_en","",20609,], +["features.roomdetails.impl_RoomDetails_5_en","",20609,], +["features.roomdetails.impl_RoomDetails_6_en","",20609,], +["features.roomdetails.impl_RoomDetails_7_en","",20609,], +["features.roomdetails.impl_RoomDetails_8_en","",20609,], +["features.roomdetails.impl_RoomDetails_9_en","",20609,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20609,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20609,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20609,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20609,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20609,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20609,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20609,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20609,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20609,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20602,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20602,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20602,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20602,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20602,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20602,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20602,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20602,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20602,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20602,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20602,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20609,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20609,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20609,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20609,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20609,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20609,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20609,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20609,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20609,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20609,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20609,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20602,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20602,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20602,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20609,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20609,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20609,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20602,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20602,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20609,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20609,], ["libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoomPreviewAliasAtom_Night_0_en",0,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20602,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20602,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20602,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20602,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20602,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20602,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20609,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20609,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20609,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20609,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20609,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20609,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1169,16 +1175,16 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20602,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20609,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20609,], ["features.home.impl.components_RoomSummaryRow_Day_36_en","features.home.impl.components_RoomSummaryRow_Night_36_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20602,], +["features.home.impl.components_RoomSummaryRow_Day_37_en","features.home.impl.components_RoomSummaryRow_Night_37_en",20609,], ["features.home.impl.components_RoomSummaryRow_Day_38_en","features.home.impl.components_RoomSummaryRow_Night_38_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], @@ -1188,118 +1194,118 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20602,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20602,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20602,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20609,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20609,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20609,], ["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",20602,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20602,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20602,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20602,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20602,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20602,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20602,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20609,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20609,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20609,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20609,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20609,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20609,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20609,], ["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","",20602,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20609,], ["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,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20602,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20602,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20602,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20602,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20602,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20602,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20602,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20602,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20602,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20602,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20602,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20602,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20602,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20602,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20602,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20609,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20609,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20609,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20609,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20609,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20609,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20609,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20609,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20609,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20609,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20609,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20609,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20609,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20609,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20609,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1322,40 +1328,39 @@ 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.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20602,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20602,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20602,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20602,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20602,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20602,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20602,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20602,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20602,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20602,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20602,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20602,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20602,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20602,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20602,], -["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20602,], -["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20602,], -["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20605,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20609,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20609,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20609,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20609,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20609,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20609,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20609,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20609,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20609,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20609,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20609,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20609,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20609,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20609,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20609,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20609,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20609,], +["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20609,], ["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",20602,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20602,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20602,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20602,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20602,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20602,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20602,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20602,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20602,], -["features.location.impl.show_ShowLocationView_Day_8_en","features.location.impl.show_ShowLocationView_Night_8_en",20605,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20602,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20602,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20602,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20609,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20609,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20609,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20609,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20609,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20609,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20609,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20609,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20609,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20609,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20609,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20609,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1365,106 +1370,106 @@ 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",20602,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20609,], ["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,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20602,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20602,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20602,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20602,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20602,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20609,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20609,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20609,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20609,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20609,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20602,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20602,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20602,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20602,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20602,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20602,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20602,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20602,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20602,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20602,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20602,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20602,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20602,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20602,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20602,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20609,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20609,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20609,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20609,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20609,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20609,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20609,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20609,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20609,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20609,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20609,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20609,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20609,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20609,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20609,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20602,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20602,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20602,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20602,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20602,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20602,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20609,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20609,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20609,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20609,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20609,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20609,], ["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",20602,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20609,], ["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",20602,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20609,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["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_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20602,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20602,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20602,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20602,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20602,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20602,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20602,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20602,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20602,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20602,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20602,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20602,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20602,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20602,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20602,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20602,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20609,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20609,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20609,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20609,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20609,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20609,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20609,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20609,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20609,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20609,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20609,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20609,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20609,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20609,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20609,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20609,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20602,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20602,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20609,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20609,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1477,17 +1482,17 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20602,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20602,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20609,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20609,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20602,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20602,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20602,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20609,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20609,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20609,], ["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",20602,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20602,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20609,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20609,], ["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_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1497,18 +1502,18 @@ export const screenshots = [ ["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.event_TimelineItemAudioView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20609,], ["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",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20609,], ["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,], @@ -1516,18 +1521,18 @@ 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",20602,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20609,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20609,], ["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_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20602,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20602,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20609,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20609,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20609,], ["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",20602,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20609,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20609,], ["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,], @@ -1536,44 +1541,44 @@ 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",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20609,], ["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",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20609,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20609,], ["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","",20602,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20609,], ["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.event_TimelineItemFileView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_4_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20602,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20609,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20609,], ["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",20602,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20609,], ["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",20602,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20602,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20602,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20609,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20609,], ["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",20602,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20602,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20609,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20609,], ["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",20602,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20609,], ["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,], @@ -1582,8 +1587,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",20602,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20609,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20609,], ["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,], @@ -1598,8 +1603,8 @@ 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",20602,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20602,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20609,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20609,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_2_en",0,], @@ -1622,84 +1627,84 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20602,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20602,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20609,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20609,], ["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",20602,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20609,], ["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",20602,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20609,], ["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",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_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",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20602,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20602,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20609,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20609,], ["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",20602,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20602,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20602,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20602,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20602,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20602,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20609,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20609,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20609,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20609,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20609,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20609,], ["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","",20602,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20609,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20602,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20602,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20602,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20602,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20609,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20609,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20609,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20609,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20602,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20609,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20602,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20609,], ["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.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20602,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20602,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20602,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20602,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20602,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20602,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20602,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20602,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20602,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20602,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20602,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20602,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20602,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20609,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20609,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20609,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20609,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20609,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20609,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20609,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20609,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20609,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20609,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20609,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20609,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20609,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20602,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20602,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20609,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20609,], ["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,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20602,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20609,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], From b910c4bf64da43c30b94f49d4de09ce0a657ce49 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 8 Jun 2026 14:35:34 +0200 Subject: [PATCH 357/471] Update release notes for `v26.05.1` (#6990) Add info about the handled security fixes --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f24c813de7..84fec2badb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -131,6 +131,14 @@ Changes in Element X v26.05.1 +Security fixes 🔐 +----------------- + +This release contains some important security fixes: + +- Check the user ID in the `sender_device_keys` property of Olm-encrypted to-device events to prevent sender spoofing by homeserver owners. ([#6553](https://github.com/matrix-org/matrix-rust-sdk/pull/6553), High, [CVE-2026-45056](https://www.cve.org/CVERecord?id=CVE-2026-45056), [GHSA-wfq4-36m3-9g42](https://github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-wfq4-36m3-9g42)) +- Reject invalid edits as candidates for timeline updates. ([#6454](https://github.com/matrix-org/matrix-rust-sdk/pull/6454), Moderate, [CVE-2026-45057](https://www.cve.org/CVERecord?id=CVE-2026-45057), [GHSA-h97m-27fx-42rx](https://github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-h97m-27fx-42rx)) + ## What's Changed ### ✨ Features * Make Element Call screen work edge-to-edge by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6634 From b1b468d6a6859a3681be9cb3302f0f73abe52da3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 8 Jun 2026 15:29:35 +0200 Subject: [PATCH 358/471] Ensure that the application rotate the QrCode if one day the SDK emits the `.Expired` error. --- .../linknewdevice/RustLinkMobileHandler.kt | 4 +++ .../RustLinkMobileHandlerTest.kt | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt index cb387a9d21..a44a8d5943 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandler.kt @@ -51,6 +51,10 @@ class RustLinkMobileHandler( ) // We emit Done in case the progress listener was deallocated before generate() sent the Done _linkMobileStep.emit(LinkMobileStep.Done) + } catch (e: HumanQrGrantLoginException.Expired) { + // Note: the SDK does not return this error, but is returning `.NotFound` instead when the QR code expires. We catch both just in case. + Timber.tag(tag.value).w(e, "QR code has expired") + _linkMobileStep.emit(LinkMobileStep.QrRotating) } catch (e: HumanQrGrantLoginException.NotFound) { Timber.tag(tag.value).w(e, "Error during QR login grant") // Catch timeout here? diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt index ad751cc86b..3d84e34f45 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/linknewdevice/RustLinkMobileHandlerTest.kt @@ -147,6 +147,32 @@ class RustLinkMobileHandlerTest { } } + @Test + fun `when start throws HumanQrGrantLoginException_Expired, the handler emits QrRotating step`() = runTest { + val completable = CompletableDeferred() + val handler = FakeFfiGrantLoginWithQrCodeHandler( + generateResult = { + completable.await() + throw HumanQrGrantLoginException.Expired("Expired") + } + ) + val sut = createRustLinkMobileHandler( + handler, + ) + sut.linkMobileStep.test { + val initialItem = awaitItem() + assertThat(initialItem).isEqualTo(LinkMobileStep.Uninitialized) + backgroundScope.launch { + sut.start() + } + runCurrent() + // generate returns, error is emitted + completable.complete(Unit) + val qrRotatingState = awaitItem() + assertThat(qrRotatingState).isEqualTo(LinkMobileStep.QrRotating) + } + } + private fun TestScope.createRustLinkMobileHandler( handler: FakeFfiGrantLoginWithQrCodeHandler = FakeFfiGrantLoginWithQrCodeHandler(), ) = RustLinkMobileHandler( From 5b74afcd79b83628b3721564318ae8e6ea88109a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:43:16 +0200 Subject: [PATCH 359/471] Update dependency io.mockk:mockk to v1.14.11 (#6977) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fbfb29110c..3e3e9f242a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -166,7 +166,7 @@ test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" } test_arch_core = "androidx.arch.core:core-testing:2.2.0" test_junit = "junit:junit:4.13.2" test_runner = "androidx.test:runner:1.7.0" -test_mockk = "io.mockk:mockk:1.14.9" +test_mockk = "io.mockk:mockk:1.14.11" test_konsist = "com.lemonappdev:konsist:0.17.3" test_turbine = "app.cash.turbine:turbine:1.2.1" test_truth = "com.google.truth:truth:1.4.5" From 9fd5277a489ea6ce2b79c3e70144b2944279cfe8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:46:52 +0200 Subject: [PATCH 360/471] Update zizmorcore/zizmor-action action to v0.5.6 (#6845) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/quality.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ceaa86016a..d460eda633 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -336,7 +336,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 upload_reports: name: Project Check Suite From 786c53ac06deaa9d947080f650900aa67700f553 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 8 Jun 2026 15:50:56 +0200 Subject: [PATCH 361/471] Make `BaseRoom.getDirectRoomMember` change when `roomInfo.isDm` changes (#6989) With `derivedStateOf` this wasn't working as expected for some reason, resulting in the dm member being null and displaying the wrong `RoomType` in the room details screen --- .../android/libraries/matrix/ui/room/MatrixRoomMembers.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 098117af74..f09447a083 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.BaseRoom @@ -42,10 +43,8 @@ fun getRoomMemberAsState(roomMembersState: RoomMembersState, userId: UserId): St @Composable fun BaseRoom.getDirectRoomMember(roomMembersState: RoomMembersState): State { val roomInfo by roomInfoFlow.collectAsState() - return remember { - derivedStateOf { - roomMembersState.getDirectRoomMember(roomInfo, sessionId) - } + return remember(roomInfo.isDm) { + mutableStateOf(roomMembersState.getDirectRoomMember(roomInfo, sessionId)) } } From bea2903093d243497c1cb30f4b01aa69226d3207 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 8 Jun 2026 16:32:13 +0200 Subject: [PATCH 362/471] Click on avatar in moderation bottom sheet opens avatar preview (#6991) * Make the avatar in the room member moderation bottom sheet open the avatar in the media viewer (#6962) * Make the avatar in the room member moderation bottom sheet open the avatar in the media viewer. * Fix issue with avatar overlay not dismissing the bottom sheet: the bottom sheet would eat all the touch events until the first click, which would somehow cancel this behaviour * Fix lint issues * Update screenshots --------- Co-authored-by: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Co-authored-by: ElementBot --- .../messages/impl/MessagesFlowNode.kt | 36 +++++++++++++++++++ .../features/messages/impl/MessagesNode.kt | 8 +++++ .../impl/threads/ThreadedMessagesNode.kt | 8 +++++ .../roomdetails/impl/RoomDetailsFlowNode.kt | 4 +++ .../impl/members/RoomMemberListNode.kt | 7 ++++ .../api/RoomMemberModerationRenderer.kt | 1 + .../DefaultRoomMemberModerationRenderer.kt | 3 +- .../impl/RoomMemberModerationView.kt | 27 ++++++++------ ...impl_RoomMemberModerationView_Day_0_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_1_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_2_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_3_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_4_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_6_en.png | 4 +-- ...impl_RoomMemberModerationView_Day_8_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_0_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_1_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_2_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_3_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_4_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_6_en.png | 4 +-- ...pl_RoomMemberModerationView_Night_8_en.png | 4 +-- 22 files changed, 111 insertions(+), 39 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 0a2c9772c4..7bb7ba1d00 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -190,6 +190,9 @@ class MessagesFlowNode( @Parcelize data object ThreadsList : NavTarget + + @Parcelize + data class AvatarPreview(val name: String, val avatarUrl: String) : NavTarget } private val callback: MessagesEntryPoint.Callback = callback() @@ -327,6 +330,10 @@ class MessagesFlowNode( override fun navigateToDeveloperSettings() { callback.navigateToDeveloperSettings() } + + override fun navigateToAvatarPreview(username: String, avatarUrl: String) { + overlay.show(NavTarget.AvatarPreview(username, avatarUrl)) + } } val inputs = MessagesNode.Inputs(focusedEventId = navTarget.focusedEventId) createNode(buildContext, listOf(callback, inputs)) @@ -562,6 +569,10 @@ class MessagesFlowNode( override fun navigateToDeveloperSettings() { callback.navigateToDeveloperSettings() } + + override fun navigateToAvatarPreview(username: String, avatarUrl: String) { + overlay.show(NavTarget.AvatarPreview(username, avatarUrl)) + } } createNode(buildContext, listOf(inputs, callback)) } @@ -573,6 +584,31 @@ class MessagesFlowNode( } createNode(buildContext, listOf(callback)) } + is NavTarget.AvatarPreview -> { + val callback = object : MediaViewerEntryPoint.Callback { + override fun onDone() { + overlay.hide() + } + + override fun viewInTimeline(eventId: EventId) { + // Cannot happen + } + + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { + // Cannot happen + } + } + val params = mediaViewerEntryPoint.createParamsForAvatar( + filename = navTarget.name, + avatarUrl = navTarget.avatarUrl, + ) + mediaViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) + } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index a9ce2f5ba1..1152faa433 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -66,6 +66,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard @@ -136,6 +137,8 @@ class MessagesNode( fun navigateToDeveloperSettings() fun navigateToThreadsList() + + fun navigateToAvatarPreview(username: String, avatarUrl: String) } override fun onBuilt() { @@ -319,6 +322,11 @@ class MessagesNode( else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) } }, + onAvatarClick = { user -> + user.avatarUrl?.let { url -> + callback.navigateToAvatarPreview(user.getBestName(), url) + } + }, modifier = Modifier, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index be573fa92f..3546696816 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -67,6 +67,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.services.analytics.api.AnalyticsService @@ -138,6 +139,8 @@ class ThreadedMessagesNode( fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) fun navigateToDeveloperSettings() + + fun navigateToAvatarPreview(username: String, avatarUrl: String) } override fun onBuilt() { @@ -315,6 +318,11 @@ class ThreadedMessagesNode( else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) } }, + onAvatarClick = { user -> + user.avatarUrl?.let { url -> + callback.navigateToAvatarPreview(user.getBestName(), url) + } + }, modifier = Modifier, ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index d4108a77e3..7574863962 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -258,6 +258,10 @@ class RoomDetailsFlowNode( override fun navigateToInviteMembers() { backstack.push(NavTarget.InviteMembers) } + + override fun navigateToAvatarPreview(username: String, avatarUrl: String) { + overlay.show(NavTarget.AvatarPreview(username, avatarUrl)) + } } createNode(buildContext, listOf(roomMemberListCallback)) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt index 750b111fc3..eec2a44660 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.appyx.launchMolecule import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.ui.model.getBestName import io.element.android.services.analytics.api.AnalyticsService @ContributesNode(RoomScope::class) @@ -41,6 +42,7 @@ class RoomMemberListNode( interface Callback : Plugin { fun navigateToRoomMemberDetails(roomMemberId: UserId) fun navigateToInviteMembers() + fun navigateToAvatarPreview(username: String, avatarUrl: String) } private val callback: Callback = callback() @@ -82,6 +84,11 @@ class RoomMemberListNode( else -> state.moderationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) } }, + onAvatarClick = { user -> + user.avatarUrl?.let { url -> + callback.navigateToAvatarPreview(user.getBestName(), url) + } + }, modifier = Modifier, ) } diff --git a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt index 8fb1ae0c03..efc87d8a40 100644 --- a/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt +++ b/features/roommembermoderation/api/src/main/kotlin/io/element/android/features/roommembermoderation/api/RoomMemberModerationRenderer.kt @@ -17,6 +17,7 @@ interface RoomMemberModerationRenderer { fun Render( state: RoomMemberModerationState, onSelectAction: (ModerationAction, MatrixUser) -> Unit, + onAvatarClick: ((MatrixUser) -> Unit)?, modifier: Modifier, ) } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/DefaultRoomMemberModerationRenderer.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/DefaultRoomMemberModerationRenderer.kt index 05bf00c7eb..0eeded6434 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/DefaultRoomMemberModerationRenderer.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/DefaultRoomMemberModerationRenderer.kt @@ -25,10 +25,11 @@ class DefaultRoomMemberModerationRenderer : RoomMemberModerationRenderer { override fun Render( state: RoomMemberModerationState, onSelectAction: (ModerationAction, MatrixUser) -> Unit, + onAvatarClick: ((MatrixUser) -> Unit)?, modifier: Modifier ) { if (state is InternalRoomMemberModerationState) { - RoomMemberModerationView(state, onSelectAction, modifier) + RoomMemberModerationView(modifier = modifier, state = state, onSelectAction = onSelectAction, onAvatarClick = onAvatarClick) } else { SideEffect { Timber.d("RoomMemberModerationRenderer: Render called with unsupported state: $state") diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt index 08d9143e0b..ff84b62669 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationView.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column 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.heightIn @@ -64,6 +65,7 @@ import timber.log.Timber fun RoomMemberModerationView( state: InternalRoomMemberModerationState, onSelectAction: (ModerationAction, MatrixUser) -> Unit, + onAvatarClick: ((MatrixUser) -> Unit)?, modifier: Modifier = Modifier, ) { Box(modifier = modifier) { @@ -73,6 +75,7 @@ fun RoomMemberModerationView( user = selectedUser, actions = state.actions, onSelectAction = onSelectAction, + onAvatarClick = onAvatarClick, onDismiss = { state.eventSink(InternalRoomMemberModerationEvents.Reset) }, ) } @@ -214,6 +217,7 @@ private fun RoomMemberActionsBottomSheet( user: MatrixUser, actions: ImmutableList, onSelectAction: (ModerationAction, MatrixUser) -> Unit, + onAvatarClick: ((MatrixUser) -> Unit)? = null, onDismiss: () -> Unit, ) { val coroutineScope = rememberCoroutineScope() @@ -240,10 +244,11 @@ private fun RoomMemberActionsBottomSheet( modifier = Modifier .padding(bottom = 24.dp) .align(Alignment.CenterHorizontally) - .clickable { + .clickable(enabled = user.avatarUrl != null && onAvatarClick != null) { coroutineScope.launch { - onSelectAction(ModerationAction.DisplayProfile, user) bottomSheetState.hide() + onAvatarClick?.invoke(user) + onDismiss() } } ) @@ -282,8 +287,8 @@ private fun RoomMemberActionsBottomSheet( leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile())), onClick = { coroutineScope.launch { - onSelectAction(action, user) bottomSheetState.hide() + onSelectAction(action, user) } }, enabled = actionState.isEnabled @@ -340,16 +345,18 @@ private fun RoomMemberActionsBottomSheet( @PreviewsDayNight @Composable internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) { + val isDoingAction = listOf(state.kickUserAsyncAction, state.banUserAsyncAction, state.unbanUserAsyncAction).any { it is AsyncAction.Loading } + val modifier = if (isDoingAction) { + Modifier.fillMaxWidth().heightIn(min = 64.dp) + } else { + Modifier.fillMaxSize() + } ElementPreview { - Box( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 64.dp) - ) { + Box(modifier) { RoomMemberModerationView( state = state, - onSelectAction = { _, _ -> - }, + onSelectAction = { _, _ -> }, + onAvatarClick = {}, ) } } diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png index adc8e30fdb..202f8a06db 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b15da251ba8ac26c61fd21079f09e3b614fd20e8ded4805cda0da180631afcc -size 17347 +oid sha256:75880628a454ca00a186e5d1c5438117e7af4ab9b23eb6fbf9c9696210234169 +size 17246 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png index 1e3ab791b8..9186ac2de4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc4a3ba905c3599ad8f6451787906ba61f8dc716464d81e8c1c1bc86023032c3 -size 20345 +oid sha256:1fe61c20e2c6a7bb72e82aefd546469dfc49bf736b6d12f4c1927628df1bdb3e +size 20211 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png index 5253ae2439..ca8464fad9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5704d02932451bc129ec92f3a9dab7fd66bb55bfdf3e142a4f3a436ad1d3b129 -size 22722 +oid sha256:ca3ee9a09bbbc69f2ba7e8b40cdc5196cc53745fe3222282d98bcf948dc16745 +size 22596 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png index b905c2d8cb..945743ff26 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8755fa3568186a798047a7fbf05404c0f738b168ca77f16afecc815bbec084ab -size 22828 +oid sha256:96971a8981aea8918c01827403933c9e75275a2fda92716f803e6f28ad284bae +size 22702 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en.png index 46dead6c87..eb96c3e39b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e611504d154db3889c1a5900e6af392ca058462450d66f0dde762ab4a45e1b76 -size 29532 +oid sha256:06ca0aa653033b59313325ad508c5916e2fedf29ba6dd3a5f714e90d01f4bf7c +size 29644 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en.png index 9399857173..9b9542917a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6135b24d94d7732342ec9353cdefb115ce5e9a799dfaee5d57b2e8e0a7ed631 -size 26505 +oid sha256:ebfb75986cd385e9837a24a5f279e07f0744bab5613fd7cbaf0a493d021c7699 +size 26606 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en.png index 8380d60ccd..18d1be85a7 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bf31df95e781eae5530ed98ab9773f3e7d0f4fd4152226596b78328b88d69a8 -size 27223 +oid sha256:a58e6ed7f10d7014c9c3b55fbb769e04619313212449a8fc582501d2904d669f +size 27315 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png index f4922aad1c..c1b0c4ff1e 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba469dd7ffb3b27261cc4f227584b7cc03bf2ddcb974a4a1150c9515a075ac78 -size 16562 +oid sha256:bc96eec2d61713f01d43c92812bbf27bc44a202ce55f0fa1dc8e8659dabcfcf2 +size 16347 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png index c79de434d4..16b567164c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d21c293e519e998d8820171766f1651d2440b18dad6f002c6580ca3fe2f24d69 -size 19397 +oid sha256:56c4df7da32b71dca109a1db1f4a797a2930e3b42c00eb51d72f0e1f5b99bf48 +size 19200 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png index dfa20ea763..ec10d3fd8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24a1a6004dc058573f264a5f02ffc85ca925e9f0c01f7ceacbaf972b3335d327 -size 21819 +oid sha256:592c6ec197b193803679eee5df7769fa4858fe883a2f3a1294f39ad87f9f4ff1 +size 21610 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png index 07547bf79a..5ff44ac3a4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf93cbb2ce344639162524714d254c0db21cab448dec38ee1d4820265678a7ec -size 21909 +oid sha256:62e99b8070a394ea5b2d666f3a8abf0fe41ea4a34a382223903f2de4085195dc +size 21710 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en.png index c5d3a63af7..2f5afc02b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:641f434040dfc9f040622fd0f17519d353f0bc45a17603ef837e53fad2b529c2 -size 27570 +oid sha256:793cff43740f6e477a255af566985d8bb7b0242d89484b3f0fc5cfec3b4a2af8 +size 27608 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en.png index a06978178d..4a9b30b4cf 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d9c0f0c69bd55b5c9a925cb89587d959998a833c5927873b4ef358959ce4ece -size 24944 +oid sha256:ad842be0c519bfd7713eb93df2a7216d159970aff1ea44725641c5db26f3ad6b +size 24995 diff --git a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en.png index 4c39cde004..4964d7d1b6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f17c5df90399b6de382af8111ba36b0ec072596f42103f76477692a801493799 -size 25515 +oid sha256:541b9ad607838fc9d44d6d3759d61df54c178257fe646366304d96ebda5f7e77 +size 25565 From 8b4158c8245fc72c2320398684570291a96a0c4d Mon Sep 17 00:00:00 2001 From: bxdxnn <267911624+bxdxnn@users.noreply.github.com> Date: Mon, 8 Jun 2026 18:17:45 +0000 Subject: [PATCH 363/471] Address review --- .../io/element/android/features/messages/impl/MessagesNode.kt | 2 +- .../messages/impl/messagecomposer/MessageComposerPresenter.kt | 3 +-- .../features/messages/impl/threads/ThreadedMessagesNode.kt | 2 +- .../MessageComposerPresenterSlashCommandTest.kt | 2 -- .../impl/messagecomposer/MessageComposerPresenterTest.kt | 2 -- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index ef0e7100e2..ff039da1b4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -107,7 +107,7 @@ class MessagesNode( private val timelineController = TimelineController(room, room.liveTimeline) private val presenter = presenterFactory.create( navigator = this, - composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = false, threadRoot = null), + composerPresenter = messageComposerPresenterFactory.create(timelineController, this, threadRoot = null), timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this), actionListPresenter = actionListPresenterFactory.create( postProcessor = TimelineItemActionPostProcessor.Default, 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 9e9fc15eec..3526a6fda1 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 @@ -110,7 +110,6 @@ import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes class MessageComposerPresenter( @Assisted private val navigator: MessagesNavigator, @Assisted private val timelineController: TimelineController, - @Assisted private val isInThread: Boolean, @Assisted private val threadRoot: ThreadId?, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val room: JoinedRoom, @@ -140,11 +139,11 @@ class MessageComposerPresenter( fun create( timelineController: TimelineController, navigator: MessagesNavigator, - isInThread: Boolean, threadRoot: ThreadId?, ): MessageComposerPresenter } + private val isInThread: Boolean get() = threadRoot != null private val mediaSender = mediaSenderFactory.create(timelineMode = timelineController.mainTimelineMode()) private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index ade22eed41..f26a47ce63 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -112,7 +112,7 @@ class ThreadedMessagesNode( this.timelineController = timelineController return presenterFactory.create( navigator = this, - composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = true, threadRoot = inputs.threadRootEventId), + composerPresenter = messageComposerPresenterFactory.create(timelineController, this, threadRoot = inputs.threadRootEventId), timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this), // TODO add special processor for threaded timeline actionListPresenter = actionListPresenterFactory.create( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt index 2a0ba9ff1b..0b3da9647e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterSlashCommandTest.kt @@ -272,13 +272,11 @@ class MessageComposerPresenterSlashCommandTest { isRichTextEditorEnabled: Boolean = true, draftService: ComposerDraftService = FakeComposerDraftService(), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), - isInThread: Boolean = false, threadRoot: ThreadId? = null, slashCommandService: SlashCommandService = FakeSlashCommandService(), ) = MessageComposerPresenter( navigator = navigator, sessionCoroutineScope = this, - isInThread = isInThread, threadRoot = threadRoot, room = room, mediaPickerProvider = pickerProvider, 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 ab9886a368..9fd0ccfee3 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 @@ -1534,13 +1534,11 @@ class MessageComposerPresenterTest { isRichTextEditorEnabled: Boolean = true, draftService: ComposerDraftService = FakeComposerDraftService(), mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(), - isInThread: Boolean = false, threadRoot: ThreadId? = null, slashCommandService: SlashCommandService = FakeSlashCommandService(), ) = MessageComposerPresenter( navigator = navigator, sessionCoroutineScope = this, - isInThread = isInThread, threadRoot = threadRoot, room = room, mediaPickerProvider = pickerProvider, From 6b9aa31c192f66b7778ba66e9d5e22d4d1bbb8f8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 8 Jun 2026 21:56:22 +0200 Subject: [PATCH 364/471] change(location): ensure permissions are always requested at least once --- .idea/codeStyles/Project.xml | 34 ++++++++++ .../impl/common/LocationConstraintsCheck.kt | 3 + .../DefaultPermissionsPresenter.kt | 14 +++- .../common/permissions/PermissionsState.kt | 1 + .../impl/share/ShareLocationPresenter.kt | 64 +++++++++++++------ .../impl/show/ShowLocationPresenter.kt | 22 +++++-- .../location/impl/PermissionsStateFactory.kt | 2 + .../common/LocationConstraintsCheckTest.kt | 17 ++++- .../permissions/FakePermissionsPresenter.kt | 1 + .../impl/share/ShareLocationPresenterTest.kt | 37 ++++++++++- .../impl/show/ShowLocationPresenterTest.kt | 31 +++++++-- 11 files changed, 190 insertions(+), 36 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index cdef735570..8151cb4ea3 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,39 @@ + + +

~WJlvE=--%Ysmm{^T|J!j z+Y$}c=Hg!<^8$6>_A!k0{0iN;4GzgiO0!TE20f;?_s(_CqD%s{rDrHR<^mp^}p(e>w>Mb~8L#lJAei@r`$g`_Lx$(wK zhbkyzhS}(m4IVf%?=*}tnL49kCu+AEYYq1_tDJL4ei3o5I=o6v31Z2T+*-)l^ic~m zOnzpy(d+8Uq}{$2Jn=NXuTAdlFVF_J5GMu6v0YuvI@>8% zzju}FnC)0UhRaOoHDA!E#9)*&wJJlOi>4Cv{=#Q+o9Kzw%cbE~*djvRxjS6E8C#=< z?aN9nd^%yyf#+gPPBZPSnLk1o;}eFACetFMxlBk8EJk|U*?*CcOvaXo84XtcOH}ga zi`Ce+MGe*KNnc^s=!Ubl!jzm{RKbl_6jARAIArPcY?JP19!t-a(iem8@wah8d55fA zS?%d>f)~U^+krO1eT`VgwX@z`GHNZZ)=XQgR)Bhm04gXUZJd}=Xq(jzTW540cHxJt z#~jR+nJJP#HFf;NzfoBO`&;0*86=zkY7Js&A_8>Yr*qm$sU;nR&0dd0t~hPi(Ny@}X=C z%V_P3)mWpQmO+cZh}O9BIquu$2DOaSc6GCikq-o38iycC>w-ZK8U`~nRbQdE4A{K1 ztxa()Y?{ehf7dy+AI_Fa^Gjq|Y}$G*$OQk1iu5Z8^jMx=OG>;}ou66hFH2>1rziye zrI3AvfOqu?{H2&qTavd}?>~%x2+IpG1#Ft=v&2KK`fHw4guMQTRiCwPH8(o<2%dby zyvX^PjA(j(D~%hx$0eU;{?EF(v_qNss|R}V;=rrT`c4Pwj!Qw@8q_bddik_hr_$mY zq^jl29(?hbuI63pZ!8R=)4xWyp^ivO@RzJ6ydydqEwfa=(2pUMj?-3~j1CIa6D{rZ zJ2<3S@U?*T&si6XLZbhhD1HyO#$)ngZ2In*N-v|RGT%VHQ@`}@~lgLL!^i)zh0v*~w2%2*s z)uJ+*U@=9hSkJ3S%9r_ z?p+aExjNiK=)_3paEEsbHa|#;_bA0Y|^j-zl3bam>**{#Oc+wXVv zpx(b`HNl?EjS44i($38E%2ua72vmJ5@$p5OYCksg5`k9tu>cPExYV$Cbr#jH1q&Co ze?w%0dHk1Z1bwv8!w{w662*^82HPjK@Y97Xg(=s-b3B&gI8)yga1yV&z+N*r*-E_v zyu97Nl?=UT`=Y-4?ZJOO2Y;13C_3mKd+^bi`lDEtkRH2J`)B*#o(chtB&Z=|&!{b> z-0>DuHo(~2jd^BUkN66=tP{R}DfiaSGN8u!ENI;#TYE1Tm3*5&ayTL(fPVyB&H7c- zHD3A?LcmjTpsVdA8G$a43P$z@t(~@Bq30sJ& zkU_lIy&XAz$#9!FgnFWMcyk29oKgmJn86Eby)}i9(=M2w=j+xYDg`Nv3!;pSP$wC@ zxy=%zKy@uziDq#JmPo95hJU@Tqz&?#O6zWZisc`94xqc``>0us4GEG=8>fJdYFi!a z;d#r~tG9NrsTwm&<9cX=ZxT#rnLxypU5#e*WKs|9uI`ojwh`B%fz~8lmx)M zk=m4xR?YDsAnR)+GGZe}Ecfr+J>-YAto6Ef( zGpFt$um2icuhdYYyfB=}9naJt#dH1^W_L9%eXny~YC#W(M3AlKvEA97OSV*j(us0l zxz|L~MfvURCYSI7YgiF&Z!-JkvF^IuChVi_`BbU8KT6QkemrtcQ+~_yMK<6hP@?&K z8ILEI06Ht{jXpH@>B3l(W!cE$@lKQTGk`uy-tGMit79gvpydpZy6 z&oun~AXU}1uF=a9;8NCGnn>W`>Zd)#6-evuHl%5`rs|fA6SHx8`lZmh%W7>+huf99 zS(0e1yM2VIq7`?FN?fki@mIo~9Xsf%6>k&r0U1oc!eN+>CRtwwven?FE&PZy(83~3y zuGmDWR8D^dNbu?gg7gkcs8o3aA@o4d7}RDZFAXvuuKGIy@<@AgIAqKJuNC6|y=0cs z%2!68pP&pWfG6k?gs_H$_ID}?$EvB1VGgr?+szwA&$u?G?S$}eMw}bX2I&irl?Z9P zw^L7~tVia|=Wz*MLeBG^C~>th>N}wY=aD8RB@~4QaqWsa=HR#)8{vlV==mfB>V{0# z({*lyghfb_4~0WC9ThN>J;tzSQzy__(pn}rPv$zS;vej6w(eEzERksXk3pSnpOeb% zr*^;}ymo~q{JZDP;JB}^iY^UjikO<(e_S;wrL@q1N`E^1Ejf0cINK?KBVE`39xSeF zNHz&SieR{T#OL~=BQ%t*Z~RxiK#yJHd;D-O`ETO{iaj0W<<@FET=qv&qQXd5kE>Lt z4A9PydAn`|PG?IHW19c1bzUNCvr7^&qV99TB}K8{dcF<_t7hIeaJ7j zo%b};RNq9XgjbwR#gw#6R>-S0xW^^qbOtmeq=A64;v5OJ^KjQV?6HG{)9-7C($5`6 zc?3FVCF6E}pr%uEm!{FNkCcn^H7}~&uc3S*iq`)w&`rSo7JI98&}#|c*&&L=$;Cs+ z&CbKXWoMF7y$~e3^_APJy5WzXT9vJNFXIY(6Bclm9pbdLEhU)du za1KHG^*_+BOtQtPRPYgI5Hvv{0e5S;7s`^%83*cZk}F{ zgXEoe1%8Vp+F6U-rO4+eGWmn zFaL4(7`xzsv<*W+4Y)4Qh0YG`7Pjo3$kzL~aA!%>Ud3N(AMAv$jOvFY;}&4Z&BbHL zUOfe0xEx?&_9_2|SJ>Hq3^Z5Tc=89(l?vv=1+{G>#+Qt_HgU$Y;kTAet`c5Yeo4qa zxmcPDjrDFC2pdffF_nG)HkU7l<16Ahs-X{G*Xi}&qaU*oB7J}_P2J7o4B;c&?x%@a zdm$t(${NvPlI83*Hc0y4y7wtbx?C!-Y_4P`Js)MoFaCWuAZweRfKRw$%vpQO$kQ|O z4itI8$tqwM?(~2HmY3|a)SxOZd)OkVix2z-ymMG*Yle=D+}IiS_Lsnn7`$wXY5t2Q zIDalm{;Q|IYoq3M6K-^qI+1fxk-*G_%Je?I-|iNPaLdgEJ~(T9#YFfZUhK6*GG|m| z(WUhuko)kx#oH-Z{S1o)YiT{kKe62FBc~?Op5g0W*f>P|q5{C_D9Ey(Dsk5@?d*X% zRI0?fpvA4b%^4i=ePpd8$nawL)$HOVGv=CYsD2^Iu;xvNo{46T_j_}8A%XaDS?VO; zuVMM{LGD7Z?s}&h>#ObL^g#3x%t+Uv$`tDsJ#OiOXM`^$w?;1&Y*MHF?d>_bcpAjs zSjg6`YbZ)0gLL+hi0jw;77?Aoz*R@T>$m_1t}Rk)tG>xjug>=LlMi(?Kst4D+F5pS0> zRrF*z;gVCk8AYkzZ1lSKDB}YN1j8Jv5mx=54AN^Z+mLE~>8!kX;cTA1yA{z-`VeNKSqe4i&*KS#PbI+r@`%;X>2k9j?4&9qAaJ zi>JDZpBimt^X_@N;@AM%8?U0jbEKW~ot0M2j$h^g(|2tsc=2-gQN7mR56GugBON`% z1c{u3)=;-rc%oxkUf1DWPu@3(fw6p&HFCO>AkRur4L$wa);`+iHt{VYI~y4B7w+)X zxmyV;>wMj!I-m*Xwp3xsw9o5wM1TFtU+*w8g5t!8XRR6d_4g)V%$M``Zm{WT0opkh zjQ?IMTl!i{QH2n&t~Pq)-_8h>lLGuyD>HURZo72XaaU3HSRrh&SAZ7rXIJYu8N3)% zzMj-p?|{RTGpU+o&g=BJRtvC_Fx_}In3{+4743eAuUqqyMr<|3C9E$i=>j&j;XbrH z1+rNkYN4F%sU5X9H@^vTyH>t77R-==a>64Q8ycNMudV!2`kHWV#u056LM+2omLPS&}*0^MOSM;6QhZ5MwTT5vozJV(cF`ECC{n$E*7$@TI3=bVO> zm8Z;=rj?nMgUm#6KpibBQ%=WHbCHu2oVZ1(OwCDVZW6W#0z_P>OjrOOX>>@4^>tKT{`YS-vZ z(JB<4!>VgaiARg^DQ++3xf~3yz0FP=$ck&5jH?8Nw4~(%zusIZ%W>ug5wSuA-5mbW zsi6_nz#W$kXZe%*eYq$j6_%14Hd8L%AIn}*KRlfk)`9Lx_}EL<*Vymc$1Iq==> zj~}vP_JqYtWHq!Eyp!3dfDR0z%bXJhxU5X2Xn2A*5>8JD75mOLzl7q8C4PPT-XsA6 z97n&PMyDmhYAT8m@kd_6GMdL-CNms4i}LlqwL;gvr|=fxklb+mu&Lq)2PIZ2j!&6; zr32}#5hj?g%&m@bbcaz(Lw!Kd$OvlDL8{eP(E!g#RCNH*dL2*f+4Vj-1j6|pMoGP$ zNn9W#syhro>k#$o-BpHj=AV7!=yGrzg>qDW$Eu48q&88$E8BV?6(;RNtE z_duuPMk*=a=KSzsEh?Ag^jY3DV))&dhRzp6*z9L;6ZZ;JBi}W~!6DTp3H2wnAiNn5dg*eH z%5Go>U|0OI4uN*c`PFO;6-G)biEqCg8r6zEda=4o9v|Av9eZe`b1toM)LIyvtL1(w zhiX4xrh9{|cSO-Ra!1%7pa37UIB3)h-#CJnnwc$7I!ESAyZKGR7VLH}tLNRwiDr!G zc@BTF`B&)Uzjjhiq{LdXHlt7^JEE-?!9Sle?V{o(t~ZWzgCj zzWj3;*D>+~J^BNvc*hFns9Yg)fYjaOvu81mz|y-~`b|PIx8Q@joK0zw;D9tq>10}r z1w^pLdaD>E;VE%l>Z!FS<#lo=euvlbUhnNuf{=(uyZO}6OOqGM5Jw8hdVW4xQ+uUU ztkowG6*JNGif(Q?vT)EngvpP%LC4x;{V`5@5h%16;?s4JjO>JO>+B$*r%)8C*I?Rc zIiWcx*%jH7uJ?;$i}|W3wkGQeuxQbvqvrf6S4q{LQI1|fg5GhgnV9%HB4nuT>Qooi z%iV_LgePxqW;4$bik=#-mt6JATlz1tgS3}D|5cV1gLrYIj-O|OWJ#8XA2o*Wu+`zh z`sywp*v;M@tR*tiu&o*3-I(Nbxg!oh+60so7*7O}hc05~@nH+Vp_BFVHnxlOxVW7Q zJt*ToNqO44sgs6sf?^vYj!~{Z=6XmS&k&@gaAd$Eo!*P@T@U-wZwfydivXkMr!tT!(#ry z47Qu{5(<+4s_(9`)KBcr{;P#KwwIqqo$PP%Ao!ah&ultzmS6VYEQeN?4ykRVtu?>V z9Gh+X^S7tvO*AUGS$CNV-Yw{1$l!xL>s!3CTJCrA>vXCn*_E?N4Wn6=`0U#1TWeqT6m?6MhB2ITTeac>&&IXcFQ1SW ziO*!>f3O9U;;ZsmqBhIjQOi=Kx5HwnVk_C*)#W`8nZi|2DY)ab;R|pM=AW1nn}UNQL@JF4 ziBhy3^L~0{3uAb zuIP#<3C`0aKUgR5Z+%{$3RDiN*jb7fAQ$6ocmKO&6|yO#m|N=VX+0$a8JpHSufAW- zTf1J7yOw&&4S)ecnqm4jBUy0NE5YOB2_*qC?DSIp%cF#QRrF6|O|O(9hi+=v2AX-U zLv!y|=ueFojkw9>SIj-TiVUW@yQ8p?FkIUK?6QS1fqKicIvx%IknRU)V+I;DdL_W?A=|u*Nv03>`A7wCFjxm zwc7DB)6tNi1V5|8-c2Xpw3{?4L;_nLAC5ER6j+MZXetgH((xC~`L~0b5}PFWfS1^x ziLG_lvio9+0S9Gmn|%%+v~~~seB)BT_=~$vI-=H8Y(ijI@UrI#M=+b;I?^~6>Wwmc z!b5)hhj$dM^^{uLo>@%n_WU8H9r#oU`|D%7)SdWF$mTR0=b9%!`Ci%FXvHR_II!QY z2S~(U{gCF&+5Bb+p8Uu$3g@*wN+GQ;|0^q}{8L@gLt|2jNq5*FHG0^N(;m*!sOjL^ z`E#4i3UATPmvHxVfX-uH=H&E7RxyV{GgjAK7n`om0Tr=Pw@=5e?=RbWv+6n&YFh3Y z#RVeb$>?Cyw!HT0NpB&#E4YHTxYqesD3ICgJ9#)s|0#}_ zr?(Gw7p1kM9ux@}nEh>YCVhhyd)tH_tgzdG6Ly3YH!UUfG*YtG4#G&?F#4L;uz{_D z-aP5iF0K0UN+qYvQVDDm$tH}BZuCm=hqradq}y3Ax_%TqXPoBOYsB;_-ds0bXfc=uNXeex*yv(`zcl`C`@Lsdt^?AYxc6_hFKu4z6S(K~m&`a?hd@#3;jM4? zBFg2aGExMxKkNd2>O=d^)($C(&E-6yB6I99vgUSu2i^9$KrWpmcM$&}{Xn3rp4=Ne zWqy?RQ)k7sK3#Q3o8KW1Jq|H)9WbZ;1b9ZBvk@Fm%;-L*$UBKQ6%P6}h(D&a#gB@R z-Az&Od3MG5unYBVx>4?^(dh1onvuX;p6SLr--x#Lqlwt=s<3=OX>aB8(y+9tmdmK? z#d;dOCOIst36t8ePW{ONdY?-dd9=)Sp;K4jDptSH&&~D6c#LyVMZ%8=pX7qnp{(Sv z0svib_CI443kT!CN6@a@;d0T2au5*c)}*6gcsIi<`F2rf9BOGy40Mwv2I!r(p{Ji7 zaV%>3AMSR8Tqc*Ybc!@=pVmuR@p8!pMax0PN8@eD4QXzO-n4dX{5Y4Hn>daTROmZ$ zF@7SQjFIF~=+IlLveI^Nb7no~W+;WQ%IZD&1D@WH7{$oVNaj_?vyYKO?nX^zWk^S# z{1&=U!#EUUsREdyq;cA06(a1>8Rc-LWg6%270wM!pr8vtlkWJQLLe~*;#I~ zo?LD@>}aOqYpm}D#E4oFZefFFr9(J^r#hgm0a(cJ+w6!mqBvx~TqmNz z)swc;H_wJ@QNuQ$`3&7b*mgth=nA3JZ>nbq;NrG$E5Fg<5*wGF>DZb%!(#q|<+F(! z)$49~esi2Wsu+3QbqvoOnReECsk=M$CG63WlJPpWY+}?>Q%iI~?IuDf9!4x`C&iV> z_fYMYH?Z5)*vOJHiU>2KU?li8+D&>hXq7z}8M$8Dm#OC{T9(tJpQ$k!!h8mH#1v)A zNhokvl2;&0mf4H1;|4dh+6N@a)2n|rz>kzxDR-WrUo@soCK9(xgtMwf%q&l$0Mr|T zvagz6b(GwAO&h>-5fH&5UIh_1O!l|6D%80GccMe<3$#GRHt5u^vPndx9j|VsXSlvz z|2}JRd;Zs30i#*h?GPYyZk^qz880JCYDx3+sp<*EU3|+(bEzhQz@FK2g@hq)QY~Zg zriNb|Ds0Q3bp10HeeplxyX*Y|bOnhg*byOBHr|+7 zIXSQ+a;1}#+_RXb3LsE@{#tck>H{%nUG|kkyHKEU6Bm?dr;X-^OM@wHWBXT*DC3Us zyL40vL`$~5n*+_3Q_27JT)dDy_zj$|Bc{vQ_O9I6yHG3 zlU$<|YG=KaXc;0Z@_6prKf&VphP_StecGD11{zT3odE)0A^QmzB6XUOEfiX?nu)z{|3%_sdSlZuqdnzfhv%?{X@o zo!M;`?HS3)MB8!x!^k3IuN;fHfdL>{Clp*}X_fPRYRBJPcs;V}y{$X#Hm@rc4zfj8ot*;Nv!?*5YyQ*ubY=j%_$Du#N zUq0O@qgXp9V+?a$|E~!WZan;vH%+*&Szw%bne)(jR5+bg^{Sl?r$&($V)Hf!Dm{1l z&(>~kSp!w)^Oh!G&`(gxQ?C3)=pfJ^up+ETrSUWVCfeF6@sHIf;KZFXN#5vln zB^|@N=LI)f(V&>#^h8%btye|V>VPXQ-gnQBnRKL0bzc-!o_H(tBxbM0^^8ezRB@xe z4OB$ST`bi@{{b-acRz8Uj$a1VSFEHBpS_Z!t|6K+yhCb)hIKz!SYw?LJlNokxSQs-^`Xi$j{RJ^zu8H-&BrgC|(!-xr3;^KcM7^AV# zwB(j6r83gn?De*}<7tV-A(c0PS8ziOSFv7^UA?W|e)6HDx%3qtB$zLp?^b3uxRjTh zHO%@{Hp2a8*{iN&5#QeDJN36JuD-9TX@)vmjn9vS5LELBJ0?QfdqM*VUGs7?r(e4& zsdI&<3elN0It4IfH7{mwpx>a&knc9P=4`M-`(X}Ps1#sETDH$}V6w-wtlVinDzg*b z7st7K-a86@cSDrYm+b#J-jqAHcpGs11LEnq>%UVK@y~wrCJ#5fJ?d)u%1P$ASxF~> zzgQWzSW3SW3Wc27(o#}>SO-)*T>h>jt3?_Yu1Fvq`LdbZhF|r9sm!h#)a^1<;sKfQ zsALUL*<-Dn!nF{Cej2x2!2q2hhc_^M&eaX>RLqk>(0u~YJ$8)GrQ_>m>0wpLP?`l$ zp|tBdd1YE1hW=feN;Qd`HDg6tn3v#@Tr}0#9o2Sa*tK`CFW3HfeFVt-viTx#C@VBm zW4};_Ab4ZGqI2S{9>w2-prjDI*BVgn>J-c+SkY&i;HDFH(o+$oZbfUHOz}_ZaHJ@a z`FaJxb(w4#nYukvKIprBTQr1*y8$FhPSH(8!&@>I0U6~R+nhXc{TJyYM2vYs4u|=N zWqiH49AGSGkC4I6I{okahI<4Lv@jA}W}I|RmQP=Y7EY)OuouX)McGaX;cv#ub#+?c z(BI)NqjiwhzM~W6Ub*PLy?j}a?L5CC9p_~W8Qc2yo|ogI9oG0HWGdV{Wiu0;{hsq( z+t{^qWv$-xNKXdvTO5v+IS{AN#N;H|3S9sz!3`Ic!8YOYBe9i@-XFsshhk3x13W^r*>17 zqJ|wlnm67$@%?VB`;Au!O&SmugS@WU^YM7IO=6*1yHy=4)vJe++Xp;Xe@MIqD-EU} zdRnw4+n^s3cTQ1Dd7H5{@s9zuW$saks=9_vkzHB0N;9E$OfgMWTDwRR=L1}I=apvI#yNak~;hRwhA;GALa>XOw1S`zU0AnT6%}-WO;mlkJYRY z3Iugu(}dOX-$d1)d|;zK4!?x&C^GYynsyZqrdphiQTcuwlqTEN6Qr(wkN(3IX}=++nr@6#hoUsZz$DR zwdoH}3OswrCjH8nQcM)BVT2>=o&Xy^p7WJ_YXf?sIr4xu{E0@c$hikm$Y|?|N((8Lqq8m=K$x8MLk7;{lD|uwOObgK8LF8NbADX)K}|6bOwp^f6zmHDt8aOW%(tA zstDt;!C$Z48jH^y>Mr75apV1$np;>kc6Xvz0XU!&5JK zJvr16cdrwnqv_h2+8U&pR9Yo5%P@02Dh`UYSEQ0ao%ySmXq(oPVQQl3)gMn3#o-_c z)l&AfgP*}x@OjeaT`+d^aa-$L>*J)E{@E6JWuG>)kkMWnt?;q~=j&)~7R4l2KW?*^ zv!V>tt)>ThSgK9E{c^>Z-?~RFe87DBD=(Y{rRQB)e*QL@tO{WAuq+r+XyX$h5t&ip z>S;Qq=SHZSNhfo1l||^=8WIB(XNVEC>4US&O$2)S1v#Z0<)e{%xs=++>$d|B$_adp zJ7>p~viOY7taC;lg_hxX!?rFPj=kPSw3+Mi>d|!7%6K@mX#3ysqQ3Tw=phJ;d9wTI zZjA-_RSp6c^>OzXaE#@ycwTSOHtSz`2cq6CTYnlZ>;PcBTfz5LmgCk4(f?YteViIJ zxj2!Ya1l$i4;YoO-?*w{7@A$%7I}!7G+5gei;i#HzDQ=P$C}^=oXgn8?xXcP8s^`Y zX}4B&v{YEL-F5ZE_@}tFbg95WB*sQFM|Uy8Wkty|jx=;*IA+Epv?Ql)Zm|nDud(Ir zGpSpC4^5c{yfqQ7+?akiPrDq~(Pjs_D}NEwlOMztnPeHxCA(ycC0G%kWvmXE za+S8F3(Bm=FE5!d%9nsjfy(lo9q-qj-Fj>=gLWM2kjk80vCGwfTi4pZC0;#Dm)JKC zlX^ju7UOJnC8$_UADt8C@TcPLe$Y{MTdfmQTEp2smHSMYPv&=%M!Ewsn#irfdBooH{+k_s!2BMb0!&g`5H#{?c*$$d1VSAB=IOT8%2iA2mV92BWl#nR zXmuE4i6^`Hd`=q6G^B@%d!;o*nG;-+bI(Dzo<@{7h?!iUEUGqn{Yt~c`9gn)>%fR3 z#~$d!7QBX-fyXH{r)|nwSzhIe_B@KuDIfiqIFjE2!!VTJUjA_eam9Q&Pt^Zohg;5` z-mR-YIGbKNA$RbvF-HAfzvvcoAw})lAF{;eb?Ue1SmVq?dCzYhC0VH4EZ=9D(%SW=yzVzAZOKb&o;_*5!b+y6#`3Cl{ZgnF;(7P{3Q2ua@ zdYXd(*=LYUyYGiAP0S}@oOE~Z7=Aw zO?1)l>th3Y80{v`%N}0iTni>x_ZcN&&5$qqPf$yh8gK`Ma;D^kqWcD%4eYtmu)~qnhr!Sjb%)>ZVVKOMx5~31VKv?}h zD;D&ptzM^SDUuS9yxYF?T*|q2A=gW)sP^_-oVPio$3F7kyUQ@(kCXEhN3gPL{pZ|M zp6M@zs+)=COwEm?8Ie}ViMg2;)o3Wh!K6E*oi5H+dq|KmBx>9Zrw)iFW9#RuT#z_C zwgR$`dg+mP#8#?jrPkyC_g({WZBE+PjMVVmYq_#G{)|aT-#w5knFKUmY)AF^Lt>jo zLG;a(<|idn8ByWa809eE4n4o(;AZ`!aX|81mxTz?NllkvW&;d|p5$zyEXzu(bY=Oo z&%s6%_5u|{r%vwMm1;e|@d7uL46WEctFv605owJ2i@I=l^uwK*EFOb;+Yoe6&Gu|Z zVZ?yAD**Ipdvhc(sPjT@yidv^9(B2@hR0Fd&!7J9g!7fPMc(oS;o@3OuILWakv?-? z%2xabI-%lgs15x4A?Sr)wib|+3N_?R?bm}!s5vC+uZx#;UjQ)Yl~L%IDEW=DxT(4Y zLLMs>pn~MUPmQ$ByKN2QJeAlLG^>!VOghonex9T2(}URxD)(|GXe+wg-0_>$cSd=P zi!nRq#G0S5v3Q@H`nIs-_7M-j`sokKFSFkT+SKc`n*(Xzv`3Xa?Ny7=?$K6}Mu#H> zCw+4p@w;n`%NTll#l+*H0^N7$o?vZtToZc0_u;8uQGawE_+<76t{x9RU@i<=FS`IQiWwIy@2_#Wn2uI}17I#cWuX{h->`@fUxJ>ksUl!5`2rw( zrp_emca)f@rmqEjr!RW4IsldG%Yp0+Kz63@Xr~-{>_P_o(TTV|K4SD}O)~#%`pe?u zjHe$2uRzlJJ2R?vEVyd8V*7`4wru${y_7X1QVMwa`>7Q76VGNUH+>?c{AXg`9Ia3P zuZH7hyxrCkvM+r((Ce;mN}3!NoL~}FR2z6YM1Ll1I)f7#MM?l2p6=a`4=YM-kpK#^ z#Gi|;^X7;vY^BH|&OP8&_pUenHPrC&poMb*c(ZW-D@%RY=3-mH0~l}ilxXHbNYcRYWyTdVv zYIG;B#|^~HnCx%=N+*D2;N%22so`x)AWt6RcO#^@U}v{+HOjAGk52pphiuAh75{C( z>usc11mxCoiond4t6WlwUp%7EMt)gwaBCCeDtI!?0~CS8(>#B7AQgcnZc z7FmS1HB|>MCLV^z={`K#`~b|joiy}gR0g~JiPZVYT12DUikkCplv@EQ`&6vcOTw%8 zOav##@w}i5YwXj2Y|0_Q;Ll95HyPTWhIY=J;&u*krr$Tf)HgJz!_zdA9w%RfO#gZ- zQO09fC_+y0d360MU$lAe*^o$=e~rSl*SXzfVfR@2;r~fm^*i?O^}Kd7R=Y$=>AS&b zUZn6<#V&kivurIw+_+U=5#JVi-klp6Q45#N_d8(0$vR-Dr4)WpZQ$9118NWOeaL=z z`;5bVNtgb$5z{SX|Mh`bo$x8c?-m5jt~d->$yjzO`)o+Y64zvQ(eV!N-x{7kH>7F6 zv>Nm}1+2gcb9X6iQqN`BsxaDC{m%Ay?190Ynb1h#O&ZeKDE^38K2rAW(wz{$Sig`A z7SAH4Vr9xT&Pr@3$E?1dnp^A@i;mrZvAIG?oWKTOwF%H3@XW}+jL$jx! zP-Cpxa#s+Qu7Q@>TPCmVOh<;dV$vsjT6&=KFYIr6C!MNtkgRQY&_Nz5;oUPXvZW(_ z_n^bRc@|{Mz7I#!(av;cWpKj2qml2p7Rcpx1>JB|Wpy)w>eUz3ioGUBBT5hSY+IDH z@a9?1r<_rX5VP5w!tI8UUm1zZ?LR8k*tWC(qOlH7b4~soO5 zi?I;r5P+90HqsDXlW_ZJ=r8he2c=z)b=D?RK$QU;3$W2cGa23 zh3s-ev1<}o{|zQt#KcehZ@so|yBxAzUbG_Q{#YtWx92Rk50Z88LaXLDa}&YAnDm46 zZNjb1XS17u0ZhWJ*YKK@o#<7R*b2OU@R{qM4u8adXD)*kd?X#tu4&o;X%|7r--=AA zQv@}Q{reFH{3A0?0?=hu^aK|V7Ih^&L!!Q!YybE8o1y0P)m60*(IW<|#a~uNjDH&_ zT4uHVbuVi(@txuoMak$yClo@&I7h&j{g%iG zA5F8U89~Wz#+y}9W4_39$SHIPTrzRXpWi*ro~E}*q*;p=yY!-pxb2y-387Rr$YDWMOj0&Et4Ye~L$=6O= zYq5kuE=jPzO3=}17vmN)0iGy-JGU(e-whJ7p+V+ogp%2c5J{F$CYo@3Z%ufWc&a5viIR77>wrQ^j> z8)xsiYU?P4-TwCSFV@qBceIQC*V~DzE_3I6w6zuJyphXR)%$*xxjgMCTm8p6)J+NO z`BPB6mTsOUEGbHn4tTWg>A zt!H}=z#P|BOunBF_3BnRTuF-YFd_&dQ$_%7VP6~rI;65j1``Xivn+CvhFixJ(sz;= zt){Q^0%XtVU17Srf$4F<&*J)KBDF)yfr8-L%-XZ^{|XS}!uB7{&f0B{qsZG6t0Y{d z@d=Ay(<%^Gw3(+zxPG37zsDKA;ms}2qUCwRe^9zNtg9db^?1x6MFJ7_S{t$}KC ztAn!go;tmnn;SZqd`*Ht+I&6)_AKGlROT`|*s{!-N27HQINuE_8z_IC{U($zXgIg) zPBXP8)ntRHVn-Q`TJ()Qs^Z>dQD$E=SNpY?U7z2GH2F2B4yxjRHtqwJZPNaZBQ@{` z1T%wHX!k^qWP%i@C|U>4;Ks|fL1S}G_9X(2VZ<+r?UhjMM(8SLiHPxbnD2w^3RhsF zS=b7OEhkQXiMicVtD*&*Xklelw6a)eB@LAH^aQ?7?s&b#Uxzd(@#@%X>s)}HA+VY z3+m8?S4%C8UGKO1{<9^XO2Damrh~H#Y@O+Y5HQ(Z^ovkK%L=n;tOh%mCYG$r;7#qF z>OTb5F9BhQ)w1_1Td`eADq?Dw!+`fNm1-Kl{?GET!;Y~Es)7Ye5k@oxQuYwyGDZsMkPMoV@^CsubRS0{G<_fUux z5JZcF6`@J%|9%fsJ0kJy>BOqAcXj;#`mGZxyKEIdY}%5fP583HQz@OA-{$<0)>Bqm zm-NRTZ5tHNr;}O4m>DvbeMg;!)FW9&ig7-Ma{tD}`StHj*7qCfyd9L7UwK*f8jKTh z>*e9$!-XQ=)XYHxMJf@JR(c+U2WHo<1?NG{!DIiUitt}O zY~P%IWC3$*lyA`G=K5hO|CA64;ZVg~*Vcj1__k5@jozD&m8CwBk4z@Ru`}PH%Gk-r zqZby%Fn+e4?U-X32WMNfsJhu=8uFh|oI<+@@!)2%4CnCZxMan?!h-?Cyqq1RH?gl1 zw(b2h>jrPctmq+1#oFQU<#fqNX!2Kt?fPv`C$33Ut!wv0$Pb%lCzIIu~IU z$eJ#Yk?3Z#SZ^`%aJbvo7z$5XkBa|Dt_C!}X0>W>6t95hUJZPRbDeyCR0a12x;h%N zu5M+wEQxr8GeA{vX2vs03%906=;g^H59GArC-Eyzl(~gDw>~TIP{^o$yYe{g2Os$c zXU{>XNF7DD=IY?LR(zBr*j9sx0ObB&^ttpBvtb=f+18vBo7ZhB%ez! z?tb*^WS_N_=!S^ukoDQU02T3(0}{F6KwftU{of%ZVj~Z-m4}~$i0F{X9qN>j%D0Zg zisrGTZHhiAV1tO=B8HNA7UZD+O@GXXVg>2g+G*yh>LYiujiWYZUhbYtj{LAl4mJ*v z-rR%4h3HORB+GHL$yMZ?WHZU&-dj&!4X!rXUim8-ce47AFz)md$18K^sh}#8iZdc5 zPYpF#v%riZ+d0xA?{G8%9pd?qz@KXRHWx-b(;iw{dc;ViygYjaw)lPKA7sB(O^ zBU^2tfWGk;19nBDc~Pb>;KdQ*=3H-p1RLF{3O}_WBu4re>HLZt4*x3}EXp1r%ap2B z`35Byj5!Fxj~Karn;Qaic5l9@L{o-GRXHYK*jlJwOnC9Gm@?E@GQ5agPFz&mxB^4- z3*PsB4u6}|Vm^#n>OM?<)o-7nL7WY%tV~hSATL2edZs|gxwZj0`9ZvJA#dnwaKRDD zsF1ENQMh@`tnyI#O%6?$1XN+g;d4meT_a~Sz*h!J*ypMqsliBFcolls^ruAq_JPS! zC`9xNAHpFBUt8GXQ*0bbNKKW|-0K~0aY&CbCFO8qCR|fW8!)*UAs(pEZPcSgJkvaNW4v-V@dd}tR}UoTnNSa1kJN>Ml_`hu1F(l=5 z)LR*HXU{x4x%aG@@%u-&NV6863G#=|lWrV4cj>cS!oh@NGFhM1Zn}DVTt5-!Vt0Bl zNB_-opC4frMbRZx^1Eq~g!ydvcSlsOPW!ezN`xU-4modA{V?3~yT$b@qft9d*pz6f zRfKYShuCsiaik|ANO90>xi@$B@~%rApB7r04U>g~Y0}UAF#V#@JmvMzk+}`SYMx?lr3?K$U@klZI7hZn-e)o4kmMb=QtuPX!6x}sg@Y}M@ox7t=4>zcl?-Uje>FYs!tJ?X8SBl=iJ2&!(W36s^Avmbsx zH*ga}wU3vxWal!|HX7*ewzldfw?5)ix_@FjCcWRSvw|%5ER|*%6dhfw3wiu_x$Pj4 zZSGLqD{lFCY#@*weL!%;RWe`(qs)_i;zfBMC2Oh3+z+8BAi*v)k$q|4Omlqn6MU_2ej_N_NQj zK&7<>7J}pY0ZdQwvYPwd>ME{xdz8*tM-Os+7~$nMpG^VcU3q|4`x$o-mm> zi3xQ615`?;C+DM)X12Un+9LPptVi1qu5G>QGZ)qC{6(1wRo|n+NQa7Erj`hbhM#0a zg9zIQhlY=Gh6%OUTl0tS?%q$;^4}g(U%t^!(`7pLWEb=<@~wk-mi8#b!mnb)0aEdU zBBm|4ChBEz`Onp~00o1PLSRR7b`9*9DyB>IaZ&agbFS-iE1xW8|1XvxER9Go;HEtq zMLKHJ777u5B|+{S8O**g;LM<5-tX-;btbDY6Z-g+uM<89t}t+&@;Y3%{AVyFMrk&i-rj)ju8xb%6?s%0aXB-!A*`GEEcAJs@e5fMs$+PeG5l>jhyPG!Y zgOdN~?++U~ZDsUIZ_)VHOI^R4v}ZO=;Y+&wM!m*=SZ-yRC$89kL_;SHk`*;I5U$wtz@vEEio29Lf$&;C0SJ#&!JdQwTi)r{Zx8nN}& z3%)>#sL`AVVwcuUt7j)3o1nPnNx1%%<(D=lRGZ3JOvkg{a7=cltx-|nSVB!=O|~QF zY9yKYq5&E?fO=S$7W$c}{)lK-J6U?q=bIE*<%82QLh7qi>16fljL8?+dbU}l#_*6q z|3vOZ8~>b(3(42vcXgPQf#mmg!8ShPC+BuhWffneB)D`nOC#M$rN|?i{oFL{YEXwH z_UkaI*9=;#9XcTTLcc^Qm|&upvP0N6kmD`28;cPv5xgtISde~~L{EoS{+@S)ZT(F5 z4TNkqNv(j_zd(J4BO{x4Brqk+Z)Mksy*67(|eRkg;L5~#r{>W7MOUUy+<>)iQ`vr%`pOMy9{LS`W)Sr`7=x;6^bSY3% zAqA8Tyzvcr#%M8@#wn*bMN}LzEh*y7O&68~oc8+%_+ei{<;|mzYs~(Z<#PRrBJQ!H zS`TLr^xmvg7?D3AUC}ul9aTna%ePnjtOy9ZHp!TJDdGHcnDbEZp`=eVx5d)wJ#V1n zSKNmweNWKT63q>@7Ak@Rsh!sxCV%f~`0crKr?+Uuww{`3S#_6SXz@#F?b@F+W9Q8Q zGrwZ=L_zUt7}Pe}3AV1z_1tFAORCn<_f&namMwb*v*^-$NIxIYLxU^DUTD(Tv0;ph zB6;&n3fKA|LZHu8=-u*Y`DI!)xbAQR(6=%&hlq1}Qj|OkrORcPI@Z}_UzKVp_t3hJa@Bdk=?tuV&`{~2d+3jzb$2_$ zmA1c~*j=)`{^G5Olj`b_e00d(A-1}HW)U;Zujj3D>)K|phE6t69Xsl?^AZ`j_E}Hx zmwzP>54~{S-+K3BZ6UkDB_)l}cT~kqClzj~#G^TA&Zt2f4c$4%iUWeSF1pRERgVz)!QB)eDIPl%(3@=r^^j;qJ6L3CYgNrcrc~>lS?i~R}u~C{ay~Q zs3oPH>Fv)^n+t;@*;!-VJ5DbZp`fSGkgEmB;)=!>54@uN%OeArsv>^Q`CVhCdvp-t zja*d27W!vF0*vH>rmR(jc_reagQ(Zdhw`VouQxq>F|e+*EGG z2A@9qy(obCUM^G zO?2Jd>s<#Bn|0TdLnk!1UlG7%9jPNvNJo491rJ=p;;i3;4vW|RAkN%_{u+f>hiH2{ zN^^JpS!c}i5VM2`0gSDVf(bh;k3z1#1cW6IAi%SgDextI!Xr!JRgA%p6LXAAw}!dX z+f|+Yk~#ja;!4gDi`+d5FUFpurNkMT2binrgC*>&z(gN@0y(suI{Tx}5FbG4^Q2OL zIChsrfpaQ=K6MlA2;*+8T^n-SF%vZWAJG>TCdu9wS*WHOPF;>PK-(J^sq3S;m9WL( zb&xsU81q#sq~>v7WhZLXAbcS~E!*T{sP9WB$Hv;rKDp~5S;b>MIoQBnA|}2=a1Tr* zAORHDr+$=`vm}`**Do9O9GZCu8w#znRdgUHks@wY+oLEwOamjUvg_HcC;Z@?+q`s_ zjpfsMH7{KBU-l}^*M{{5>pWvphrn7|R)cNf!mRcFk?`y11wmiA-Or2X-3RVX{Ah|@ z>a^=7cUPQo+Dhzd?~@Q{#I4vS4F+yJ66QQ1X0rzjALa~iCNx$~o+vxI>N0Z!5YfM~ zP2Szs2mLR!6e>P!Y_E_Ar4j_*NA=}NepR(kRl{Z*U@~gG05+j~&QY?1Nbq&5D?jF( zvpD;B>t9k?4%B^bn{KxJ=f&?42>*V?7C$aN{N>+k4;?3Zu4Fvw)yuzoZTjl|LtjIx zkG&{vNl=x5ME&&?m$&dtDaN$?AA`n^AEc3G_PWL2M(i%DRj&eN#}^&y4*7C^Jelin zQ#2v}VXSCR;B|IDqXHgTm(67AB*`@r;cc-b#rUXzyD7>=4e#CXRZDo3=v{Dm$*h#Z zlT&FJ(-b-W%)zgU#%r71dz?BUw>`_mUF28@VE6Tdr0M+SIo{e+bk!c7w|!%0v?Z=j z{)jTqKF&1IVQg=5I`W0D=0r*Ctk0ZAc7#9IPSYrC-8+BePzOW*ys2uQ-OR<@{$V2b z>$9=?M3X=I(^t3)J-z54xYCiTs17y(g_U7{PN+}{c2T}8 z(K~x-3HXt3>Ej54fub?Lwgx+DLrLi+N`Do2Jq|VZKGa5OCXs*EWTMv$TpW@?*eh2w zpXN@ecZ<2xuW+_?yKRf`F-d-s*ckt73Z;)tc7?kON zEZL2&7tcONCXA1)vg7xDdUn5uZAO;2)WEMQS;h6(8!zZ%;m;D7=#Z5;w1WFj+?ma%N0Z z>awoo>OPy&&0PyUd%~2xy@B1`(Ap3lAV6L3iQsnuEq+%PG;-%i7CBZXk0)x-PO&0W z&QHP=$G32(Ui)_p^W;zYTpxbM_8;>Pq&{_;ruk7eKoUEfDqJ~5Mc$5J%{Xw7g@Iwmgb~PzHWU1|^ z^1>tlyBt$YD=2=@`#SmUVZoUJP;uz)%rIVgXTjXB5$1E;+K{D=t=KUlbB)GKV@s|J zq((?Hqox;52GVW}HEiVa!=w{@w`&_SEqSRrh&@^&4j#aJ+<9IV_r zKvhN1vF!bzs>BR^9rN=!goUJ=_0MGG7}N z6f|-U{NOej!;x=SREA^#mE2O+?;1AD-aO2`Gdb!aD-}7i@Lzp&PC##H zz;Qt3=Ma|5l-Ba~vkC}^$3tC!h*$c)isL+I3}bfMC_a%AmfI}L@2LV#{!RCo>5o77 ztz@QPz8bH>bu~1b2xvjC5sC*}!Jl(C3xl&|Ia&r1HEE80P{T6#sNyVbO%;qNEM$%E z%z;5OXJu#eM)K6GPt}z0*6#%V--N5PxM}E1c(tXbzB@4*{)CV{+%TtCPlW6&){7NY z$Y|rvvftENZxqwL-#n!*5Z4A4^4>~D^#A^%OiQ!my@u+v$MHw?HrVY!yIjahpD%jP z-o8pi`*1E|*b(bz{5=aBxxG0}!Oz46hA$t(g|${IoV}3n2Bq}J+LJvBh+cz=Bu`DZ zz!Ag8KAck}N+mMieRKO$O>LV_qfWK#8@j5W$M_amNjr1;p0JFqk-xtGt%*BS#ACHQ z>;}?VXC;(z?o7J5#2or(uR*gqDn%VKbyX)(pXT4+o8jA$m+r2Qd%H9HXYq*5h(Eiy zuF%q9zD@_s8O+M=u&s;@-DfN)EvAM?G#42o{}O&sVYC22bc?|AB|1@UHG06{k+oD` zy-`ctx&r*X5+{`M|7d#exTM?n{XerZ8&X+W&ZgFF>TM1jh?$j@gL-?XIVm+U7jBVh zS(!P@$_0$H|Gw8b9im zLkh%G*7e=gUu_ghNP?zilOf5a{|>gEwp&$bPuwhO3hMdxZQ^C@Vc+4&pOUoRN9$&_ zQ8&4qi`mg#c5Y39hMOCy#=abNCvYCIzWk#`w>BvSZxPU!NyMxu^|n0Y*;fNIVM>6? z$TNmRGr4K&853L6Vv$&U{Fp~~@B^YeoAwV&3)|?`FJl>Y+zfT%bu@D-rbS-Gyhfx& zdFEl+-AC2H!84#;-Th5CyKQ?pPL*$==^>6uNynUhg@cH<=mTV*Xj=R?$#_%onp8#- z`!zm>k7zexN7p(BuB;P8xymEu+T;9z^5y@t>o%Kkf+j=Kz=fpgTTL{j9qt%>X9Et0 z3I0DjmtU}h?1lJlEzKX^9y3*BCyf2oDq<9Dgbi3*=_er^2 zom!CYb<|i6#E-hUpO@XJ{%zY#o)yarj#WTpIkhD7VKvjGvtGa)L1LXJeXCRe`KXMM4u06+1gD2h1G~&1$ zR54Kj4H(pO;u{`jpH z!Ped?9|zOTNvXSmJShetsHGluMR94HJLMEUU+OMp)qBDzz$!C-`6pj6otYMa%_=my zsgagF);L?_tztG7t{*bBdCx;_oBty;4k%{JnRfCibdg3icoBRzevroB7s)uruhMBJ zQV1yS%P3sYs|7?N%5V1q_yV32dKp=Q&!4fN3ilj4FENxUjaBVqz z^`ynN3?DK;rd*B9qX9pv1US8@nu6#GAJ`ZSMJmB}!JHObJ^&6od9|3wg)v(yQ;5hX z@w&5w2a1Nf98@f}nlBpTlru^dk&V_ODU#y-#(4a)@#uEt#2mbEDpcp3)k0IM){mND zv&eu4qx35OK&BU#R(ad_Nl!nhmQMDa=GJShYYt}Gg;8HhI3G$sxbZ#E?uz?jmyOrP zi;9dBMHl&t53hag+_g(K_s$KI|2(j&Tj*3&bF6K+96Jd{k@|Abr|wYvQZ-n4e*V7EYy*$H61pEhw$C6zeLsgpp`wh%i>4gl#UKgZ0e=)vPvMz{B0gMecy9@icI zpUIEkA%(}nkJnKSS13yxXPXE{+`^*Ztv)f(Rc{xQw6ZUZM-~^1ILKe~*CX=396b(( zRI?P{mv{W3w%bc#TGqfIs}jhktzU~UT}F1W(`uDijI zI>Bw%%f)MHg()?x4n++kH>Oy|%p2n~@s5ww@Q3|5S7}Fg*NDzIbmw)%e+&cGR+lEMhr|waDXHyzV{52$lU(l z-2_qkMHn-QDq~m`UJ;hyHzrxcD{`r7ZG-Ub(_E3+%kzz~+lnqzqvE-Pu^ug2`*<~$ z%XLXE+OB_M(f!e}bnJEYHHCPL`(L3WF7NTRB#>9oa&P3TE3t~&rHkLOw!#NDv0@Ik#Ub)IxbCpJJzpPCxXZ>SX-JRI@ z@snOuoEj5r9j zNlRsBz&r1*xUnK8)^nm*FJ+=72gJSTQw?p0zt-Mg{L)W3WhGd;ny>gK7IM)M%C&7D zk{#nF%=EJ>b^LjqwQBZ zuCO9B5wu*8vov^zNipJu8^?AqB!a5V+|v9jeEc{?WOc!VO-4tSek2WYBvxN(_TEI; zNbp5Puh?uy0lj4{>c(bLz(B1wPpFx(bMdi=suP_(Wg6Q8rU)L2_l5O~Ktp7)qEVHP%nD(dsH^Px)bRW@N6WG#>S-VBj4hvt;iy{pc`#Pbbc2jDGYsaq zsZgJvk5&r(j$e6mdj8R|Q$dIN>LJ9_qlc6WUVM&hlEz8dX<3nHRO)Lajs|hpy)WN$ z+hw9v#&UY-eNPRkog+AXg$dh>)E3N`S77;%^MV89HaQZJ5)EeV0o;115cT@ktGE^Z zn>`)x3*#6!0WrU4vLX55*B1>_8D?@5w}LoDo%PH$F;d5YnHL$1Z(3EclgFH!U2V&{ zKRfLisIau->_wYvjLw+C#$MjB`02DQnry8}VE2UK=A$2nwNlX11?-{Ncs)qf!hh)K zr~Z~!7_=yscc_yg5BwIrj-FmWC`p80fQCL(lPjMG1T*HuC4^@++LPiwQvYcjQ( z$z@8ezDr&kzala^)o9oQ1A4OH(?HVQEyky<-vI3DNt}qXFRY2V{ULjX?!b3a?Mq<3 z4Ft(?28Q;>EUC;DZA;^HmCM60f{v9wZ0vdCKUBH=Zcp==HxNxT6)C5Y#RvZ~-b};{ z#%L;RT;Y96yXP9zJ)NyFHlQKR4*LKx#!c#M=-WOQTn~l*xf4=CCiYtqsp1)F0hi~D z;pn(yc9n{V%?8VTgcp!X2IKg&6u=s3Fe}xOXM@?K26hv#l?= zb~$ERVa>a(NnzULPnF!lNbi6~+oKRvSFaiUY~8^uXSw#8KX@kDjSct;PfMp=G=7sL z`?*+zcX=@O@Y4PfM0zI^KdVT5f_oFmm(p^dd4H=unD8)1LAM;={$RZD{nmt+knIhX zdP92n0DALtSYb%GTpNz3S@4-uaMmGPf%g{EsaCp}C|P0ZABDf-8jI!xsyXFl9t{nC znC`ClWpJg}Xb`@hyi_;dsas~al99;67f@v;h!r;i$FcpNm(l0C^%H9ry|Hhx^rx+} zgtbwF>hx-@Tb&B9s|Kj3*bGfX)|CyXn>Fgwn@?DD+rd`>&A5tu2bURx!>j)R8OOR*-)-o)sMRVN=R1SY;TEvsB zg#JA^Dz>I&NEO{~nUN9I%T-=mD9O26%)xi-qB7*kmeTS68@7UB4j!n^b}>#94;+-5 zbhgUhRU@F|3iZlN4p)%n2|3`mmnbcrHbv7qQp+p=_7!na)rrnX z+&X~7Zt8}4Fg);JZf`gkxw;S?rH`9@6S$G^G((Z&8G%vXbICPtgdO&72}F%#2sZdZ)qQ8P~E0a9B&z zlZM!)`a2?r~eM&Cp9Ybnw`*gL!E>!UF#V`6kyqRBRZo zh@6K^IM@+!w`gQSOQzqV(j@Q%?>3fSWJs8kk5d?fq!c8?ZVlKBM|bw#?EFcQP1j{8 z=LMFfGrRT8mOiB5K#eUGjDpkC-=As@h^Fk!rU7Vr3lzD?I!TDMKqD zfx=tZxq)UnG?D0G0{3TI2jQ||dh;FMXsAg!e|>C%-iA3Cj(*byAb#m^4`S%-3pKud z=FRWt*F)#`A5-=s&)>a^;HZ*5-909ISmSi}LK?g|Qzl4E!R_uphDeEviX}6lN00w! z6)J|8c~C-47htyU0+*ujYAr6&U%I@ex^?kahb~DCf2E3iPLF8)^g&i$+rS>Ycq7Gs=L(@3Av_=5%7HYgctI$9?2xu);K~zAtlML-^s@mQg!e3+f^UZMXZ;EJnn|58<1D=(s3HJ9T1VI?XzaiK)63EJ~u~|07 zO|QK6$5c`(R?vh)Z(W!cwK|Z>1eN5>2jUZ3wG-8tb;fB5f}AxcH^C)$?qqWNvcaX) z3ll_T9)FO8-4vy7N>>AGrVfuN`JfUIT@k{G?(ILZ@G`+BuA0PB{)VcO+x+f*F4FnY z_Wb&Cm0?^xE2072bp$}1qLDQ98#~Z{Hu|%UJioPTTK(>uy$?quo*(;fo|OhU-}U5E zsU%C8BA-V-nI`AUwp)`rE>|mkqac)Z;b@l;BaryW!f8IQe85pjYI}yE{B7N%<&KOT z_4h4|_Q>S?%bKT|l@ASvXo3`wEg|2J4J+8!sg)CYwGKX(C1{p(eGcDAEbMh#$2MWRPn}##*iY|H8 z^HzKKT9((;9pVWB+fL1Zvh@|UuxN5qv2Xu&i{97B#f2H|VbiNt^{|~jX-MI9*Y@SQ zisIAi!vh7a<1>WP37A=hH@&)sN@4Gx2%8`O?l>jh=pEPn{cPzB@xKY)qr9y~5Bz#A zY{_|h!4|H0LqcF$9ZFow7@@{TDw0-by#6PG-*M$zAE3MsnZqC<56u>MGetx^i(&}h z9p}tuI7}D_CC~t57i*{0W4oCzSD*QIM@#cJj@Su7gfwe~YlO&6^V`M%$6kv?x_4hf z?vm+vRU!P}wY)pm1RqEKJpq-n5|VrMTmgIBIxh4)&btxx?=UB5A7r)^Z85p5 z9wH~=nKJuhTC%K3*NYMx_l0}`l(Un|Z%IH$V?uwueXG$z0By}=yfBxL1$Y>JO_`HU zC9g)FscthIDYiA9{j|4NWSBwf6A#Xd2U((epD2u{$NKp4?m4ui)TGsE)y!<2Jx%@h zW*!kn!u#mvq9-o`scxe&vDTYQAa$F@h=Ksmm4`VeNb|M?dw|B=RI^so2sXxdV}RDJ z)T0wN!0KRMlpsYP)mt*ztww5yixu47uSBbd)BO7OVGTwc3FNn>}ggOoviqx=woQAij3ma|Bi z;&|A%2`jOuMc)zmE)Ci%b=|W8L?wG`L$Hy1Ku96PcsY;flI;B*lB$2ZyfWP@;CKGQ zMOluj(rAX1C~EctL}(~K)p)H92QHvZ|EF{~?32O0OoX9zPqeZVxrIWc4@iln{xGgW z43^TFRr=K#Fb}?GqJN3CD3Da9F>lzUxTh(b!0&##^p<_3V@7n~l->!Y(gF~vjvIv& zr(2Z<50p()Lbk&e&E!_V+lm|fyVgv`_gC(DHv>On`_nX9XsV(~N~gxh#Sn%Q{V#)$ ztmsynI;)?L)0&%!imZrA6hxULJCB_#nYPD#*Vof5S22XWF*FLQOKRd=P!TCNaYF{Q ze$duWm0EgL>2-fQR(Sw^Y%3^V$g<3z$=`nQ3KyUo_&lVOamRM9P`?g>ho>MSnjv+3 zjRD2o*rh?N&nAz8!*2fHjIW6Yg1@a2%e@34XZf$^7z*9t5B?wEUV`e5b}kWx!>i`m;_|1WPS;?|-;@|U#V0O0JZ)7uYj#P<;lfWo5PVE4n` z81cYeDxHYgW@}anCeHSB%TKsd!PJA?*~1xnT~8F;tk(|fuXZqD-jmI{xjYl+!|Iss zPm$-F#0sc>YjtU0_#X|{Me2H#uEe6(6$(Ao{-hbP5cln7AnsY|N$!KrhHH-Qf;WR4 zTz;fR+4|~ypQS-;|MlR?R$uG@Vx{xDO_t z(K480a?O`@=avEOFs^?a?2y%I$}*#6A9o_K6PJ30X#3d|vmEuguyiB!$$%RndT6S+ z#DJb}b0E=8B`tkcZIZrP&R^;B15fbOuA}#G%hmC!T zU^DRro!7U_g^NG9%C-L3cTO>1e$y;AH>Fddfyg$8@gk##pe?FM3hZW(97((m__z^W zGoFfJClW>#FJaehalxAr#@q-wVT6)kQn|;tRzM|eQ)6E_mW63$O1xaWEcEEJ4Jm~2 z#h}|yiWRZDcg76AWcZJO-(G8vdT~Eo>}*Y#N$T~3r(|WGgvY--{-oLHy<*GZcWzftrT+3 zeq?`~vrWoWqS0)b?$*o)Irhvq`?k6HT*yu=uhS;@f%E+9&4k$AxHegsN*F`!B>F#b zmoMbZ*ySSc3XPdu;_)r-r=6jtd;YMDh4T<5&56UA^$#y zo)-z7>$ZdE?pstvl^*0cLI6kvhn#VLz`>l}XXxMEvF`i)XWM-CVRHGgp`w-C<}$$} z+YmGOa{FqiiZ5F^zHq$-vTs}v3nn<&W)nM)-++NfzDw>kQ!`@MA6+IVztX4U<8 z<gt@AtC`@H8fr%sFts_7U=R?hLT8v*^$8vMd51X zit}dG;F2Qiwb>$A$ckSX7jBfp%L*aZux0;u*)Zs4@}lk9>g>OyBFsi$NAe11@^IOX z_tKd!`^BbYZ%6r?oZEk4kH*}3ZMBTCqE{j|?TUumm$pYmiDKCw9wCP=-+Q-&@?bS4 z`dmB)*XEyKYYFEwFJ=!}jJxcHSc{X@De0m{-~J0z5Erjv6$#D>jcuh9-fW<&5~OU+^y)}0Y!Uc zTZOdqW=L=R%id+wPBlPiY4y=#8z-(s2y6UDlHoKN;c7I+9>}q9rUg0!l}X%hIcT8D z;6w50&XbjOwqqqyw%~b>xX=Y{z@LJ^nlt)H8HHdks6La<*FDXl8{KZVZviu{n?CbO zG~_t_=ZTVu*y#e#D;u-1aTxFu>0!3*a;|-A}Gw=2%$wT(C+@`+(j;@ z5Q83=_9o4<$`vsiy?J(#OiLY|cNwd!HRG_BjXR?6{) zutY_FJ2o-BdurGm8ur`O*(#>{c^b>#wPvpduZc{irsD)om}KJkZY{viWLDh%I85CT zHODH(&}=gOG=2Fq8*+%A9OI&es##tGX(QHts~p4jGfosB8ka@5y2c8zvzI2lANP7G z@gN+_)ltD2PB9?`m?f`J2zpw&+RXSH~|OZK?bdg;aY%lBQWai31<*jyxQ;x2q1DcP4e zbWB|LNJ2`|lmdS<47-V1K`PxbP7rb$0=)qSAm@%&SyaF(nkkh zyJZmyrx9A&=JZhu)_EngsyE}K1H3l4aV%uSN7a}5OcVXc%0@i2mg!`Bnsym(1xM4p zccF(x?A&-TR>a<}r2|?<(Q6%TTjQI=+2v5uRzUq>V6H&4$4VrLevL7c^U-tG$l}6t zAsUMo>AXGKx{OC!P66l3Bw37XMRYI01s}pVYg(Y!(5aZt{Q`et^M{(W2yxb{TVYtc za{fMPE+AR>LlwiHC!5s#^}}olJXNXij~1*C^)njZvZEC%`-<=5vxw+}X8FTYnfw+~ zi)&4z8OeT|gW-$fCO;F~E*ew}fD5b~r=w4Cp&HnwOz=YH01^F~%KhKx^tTjeSEDWJ zzcw1zh_QyYtvZUrc71FGT9=Tp(<{SSB#$`4ahOSQrH5b<9>R}j`K(*kcF%i*7}Qiu zh)U;BIi`>Ie+g(bJNT1pKa@D zF`*wvmwZDVDBz04o}}gJ$h2SIR0<@hn#=shxyp_j^G`;G^gXO@2^eRRw)2dtz>c3r zPr?W7D?PWnTHpGIX@U35p2`cRNExLH3=KJXd(-@OQA&Cpl`N1Iy+d<>Tk#W}{`c}1 z+6JFSWd^qm3VIE<8r)+j24y=9`wKLdUH(xD+%BTwOV?&qiPNedvyGOXwvl+i9rv)m z@N5k$77l_%FH493eAR87AbzJ%PzMmyA(d9Y$kcj>+EueDRHrmwyOk(WFK)RSa4e{o z{TH%s%YkkOw*4*X7d;m{nHc=mK3IPT5H}f8T6WO&FRfi$P|~Vl3c>d*29g> zP*r~nm7s>0Hx6_UT}J;=5zVhZCT3^&&rzS(F?T#4MRZAbX-YeuQyEL{++9~+__V1p zUA=5iN1S-SOG$zNUp5u}>~PQ#UqfhAwFuLmp{i*hB|_LN8chpLTr&=rKG;nE8rAYU z>rppLB9S1Z^ZpH#Q&%*Y|3}Jb$LNOl(kF%IPo*)#339}>k-ep+t_y#RF(5bE1wEuE znxNUK>C~9+45nL4aBY;wd%L2wcKR$)rjDzPb%QM^S+sE!0KL!Usdv6r!}qvcTUJ>J zLS3Z`Fi*J^HfO81etwP@{jq=%u9e?WG5xqrua;hiW@`_QIH(`ltfXLUh=TJ;hroGS z#{js@L7HStePr79M?ALj5`ZP+MeI&fD_Lb-lp*rIZJ1FFzph*d&%h1VHwa#_;4oe= zdGppQM7IHRnpPhZIK2ip2f}22o&GL9$!}5ubvl8_jFwlGtg~nX<8*$&+4$Lql30p|?eGscg{*rPFPs z?ed)v9#4eU%pQg#>g|mSM9;Fx@Z7(mf}K`Tr(idVlPKGxl(804An&h8F#8wS4xQUR z+$N1*eKC7K9y!Anb?*j;QQMr$1b_Rc?MKF6REga4LOB8nXT28pG0hjbVnqVF84WE@ z)Qyw0}wgGD)Vo( z4#;^v7L5>hm56@0T5K(k80u>6{d$ukMI=)b%=b<-l1*>uTL4pL*v#uj1nTXi5hX}POVR*Jz9X4l470iT+TN-s;*f-xFm!kR4P4;{J4K@x9w>Z1 zaAgNDWlI*X=~V*JKc&Og5<2so25){E?-N4;)mDRovr=Gfh=bJFY~II}IFKzVmH8pP zYTVgVu{9HIsnE&r8;DjwkslVv(O0Kzo{gEW4+yk3%^H=Qts3m|8xLd`Y|xB+{Mm_c z9xUIN>vO2VU2VzOOL90m&(*-aW+tP5*&Ot`ixn;bdc<2O5H8I6$D>An))Pe?@jKhI zb`<1*XCw$eXS5R5M%W6(`C^(8)HWf5{nNJAGrOtMZ*CU5S-rykyNKGLg6N$!Hj1=~ zV%E1XMHj_cQ6V?SkJ>ma5>Rq@74fw8g-8iW1K-+p`mEw+MN_5G@681p@AC15jyEZZ zYlF|GL7P!8V!xfx+xtmhzZ;m6d$iI^>)Vm)2LvbhlQSD(9ZnwTbDCkR zI*=r=gGV=$)^-cArq&<_2}EZM`e}^^B}9?dV?r+Sqf1rF1+BnZJ+Akm)o?`7;|!Kn zvVJP9rB}~rgjX^mw-4>NL`wpiaGcWCo*A!l=vreAhScaRl~A*nOI1Ip*Q_#hGBR1W zNcWU*UOtCgYrU$r59FQ(5j!J0y(&YCn>FCeb`(x~Y1>EaLF(u|!vpcmSddp0x%);blzl zGG_m_0N7ToO_(+$5(G1)zSu{bC$ep8rXxev1J^tgeF5S#!fs=}S#{)_Ej11F)WIat zHId-N>`DU=tNt^ZCiP})5bW31veL%OdxN(KhyP1luQtriXcwj~N=G@(^yL~1{$8Gq znYg|G-c@^#wNDNH4MVTQ==#|b7d08D3Be^>MvE=iG~rOH zrXd2O{n0l~)@LC#z7cqEliCZ1&P@4sd#LM1Ze=Brp4zBh>xAmc!XdQ5`b}ck0LDe} z)Ph9j%o7K%>368Y*Ts$*1Agwyydl|#8ify_tK{Vev8YD||5+FN&bzseQ+hrtPximi z9BsmkHswKK$iHo3*bh6AB9AmliT75C+TWR1t>v;K2_6aJ^-ZIQbEuDFkP{z8r4R|l z`>lj-L_!zF`&5o`=gSmDg`F_=!3OBmXh55={ESP(gicC<^qbT9dJZdE035e|@Mm7K z=F$CkuZW4=RfKuJf8S`Xj;&ad&u{d5`yL&a+7VL3t0{%d zHKah!02VI3UFN(54)*MY_Vh|L86SaOT?h9ZGep)j9)s{X4x=I9uvy86Dq(@rR|A7@ zf22?eRd1^GxQ88Qr1FgH`H9~)FKu(+z42dG&|cmU(MVM+d~tu#BMgFIkTOscme*yluPDT}`e&OqhQ-ziZZ>1qvQK8di{-2T@49d1Pk7P()rIv(b1m;IFwD94^ z&>n|)4UDzBp%pfoG(N{Z(hT*2Eh?MGIhDI(Vc`l-5NaUrF!oqqI@g=Ri7t<0Ugj>Hb<<&ouw#y^a72HIO4WWX)^?7|_XA36^)YxGs zb>gApyQ;!!VXIk1!4+*zXk^iZ77FTX7u@xvc4*&p@J_!T$+ltnNhbv~eKcM#^uuMu zA+aYXFn8Qz#Ly=!t|FdCX?^^JpWL$tD1GIuXi63Rd7${k+k(BiK5o*rnpOJy2hlh2 zn9I7>Ko1wgJuPnrla+j=)&!k423$QP$~M=^%8uNR%}Q*S zPm^F=g%I#KzDtR+mYFpa6twyzU(HcB>yTiN&L3s!QX=aWA)lII!BzJW>===>)k@W|jkSy|N|{YrV5wZl@pNvM(0vRbj(2X^4g3T(29 zy&M7u4re3M+t=ZU9=UQQXx%8kZ?Zs%vjK5;QgW>FE*`W)^7dZP-);=~xE`WA$sPS14}{LQlY&;8h91+NHP<|xH)x&U4A3%m!Ht^B zxk%#WliDG>!B$Rz@&KX0Wbio9?{<%*#E=+E4yJ)yl`o0_HfNRY%4<5ilh~~y6Q{5) zZjcvUz=?^p#-F-0LR=sQFX?w~Q`M z&bK+#>kKY5+D@U}iIB+&_M}uqJqs)ogWY2DMRRV3b<4?}(Wa!g*8qz$v1l1jZGLY` zhZXM^^)L}8igUIo(Hum2aq0I0XCH@Ei<4Wx z1l#=IQ*?3{f2}0~z@ITO_7qJY>*6qvN zi*wgDZPO-Iw6hDe4s|N-wVp^XXeicT=)D1l%_T3p^($>HX>!SfKZRKZGO{veyy@a} zPx5^CAIpr!>eK86 zH*_EP@NV~sE0N)m`}f{6aSPhedd&DV_)0qb#{OOTHx|Znd*Iet6NU$7uDyiGkDY4Q z-&*u#(R+ah-A%Z^^W7}*KU6UD+{QYBB&m5v# z27%v2XKTuinD9*Iya@8$b~RK2tHYnwtH1Ol6MWhzgLlJOUTa)%4%G~XGP)^4#cx4W z29)R+jfX#??|j9!BlWMwO-sV;gV%hcgfSZ;qe!O+>L1Gm zt9kxLO3(f1WKZ>GTd%i@pnkoo=m^k_$TU8~bG&j&T2dgE(ehx^W23$AU{XE<2zu>~ zel3NQPk=$0F2iU(Dm||EIt@r@)DA4npoura)K}bko+*k_4Ejc?U*X$~*YJhW;Tz(s zzd4$tclbe$KLvNZ9S`GEGE?f=d7H5kXwPEDa$l8oO+)Xm_4sThOQvIHaSgq7BC!hYrKyigF7o;O$`6SriF2;zEla+XaR zo+z~KMGw+lF5A=ueHS|eBzIz9uiURd>_LiWhK9D^eqArn|LbAdjk7loZJ@Yrtgy(*Hj+1f!y$t~(nRUeD~(Z^h+n*FfK|E4y6 z8nMi|@S*u>VjNmqD$9rnnOLwQkQ zN}KPP6L$`9et=y<%fyaEAgT*v{oEQeC4L+_>v#03%W$(n4e}lWCWhTnzsV|c> z!XfEMy@To>1#L}FqtQkDr>KjI&dn0m5^^U~p$AG4MWMHCkqavEnuI*Nsy9l;(6x)k zWow*;wezDTdu`ulMOY>{a=r5!Od{vJu8|xsC}B4ol$4i{r=uaUQ8%U9XaA>;TvdhH zsyc`4BDIo`Q;{|qO_?fz={Rc@tl$3fY{y}y&4j=3b>8;<7(dzDNr>91Px#Ewa!saq zb!LCq*N{xwipy1{3-ij+5G}GqGvqal|LYsj_>Btx{&tKc0GGa5$7`67M4yX*o2Q{| zJ=@hqJkd)6aaJRhrK`L)T`18}{YR1XuQK4=(Nq2qKNFqgv@P zD%mf7sdxWmOjaUJP&H>{j5H!_!1|U}A~CzCNRQT<#@T>X(0`m0*=Q*4{IeftE@_Cq zrj*b1--<7r%p>>QeINbvmXyfyj{WuWNctJ`t6RLQOF(dI{K*`}osz>8UpXzgRWZlC z`2^rT56%f(73*|#<7V3IRRu(&j?bqjX=4mHwoAqWHKiuRKHKyrt?fBRv@78M|mz`~I4i-2s2=y*$Sr5n19*>_|);0yV7FU&$c-sh%zrLB{{Qfhdd7D2$Zjq<9ZMyyO<lnpEV3yo}=8A`!-8rv%)o z>aW*if1X%myJSxo*&Z?wi`hMRX}4C_aSM6;fzJ}Vt-hIEe|GTet&eL+{wQKoa<7ro zsYHjDKn*kA(GkZ>{>f`*$Lf^K$#HnA8s7cgj3IJ*9ug=E7B-}*A=??s1;3x2RvJyu zwkCKZSM9k^#gz;$k@JYtX`lh8LX>ACBvjae$i|JZJcBl`gXLPRoV7vTe^g!Wp_wy# zG^XEs1&oz+OR!C(Vpd+?i)*y8q#6gDTb9b3mr0ul2svA+mf^(wmB+FZXACI4B!IFX z=f-jWagOv{38%{;*W;RqoM6*?hehi-1MkO0eorukeK%rAgd+M-eAaGukOQ1wRGjTR zW?ROSWSbreBwB@NXwm&5`FTWBkW56e`XjpL=t)v(Y<#*-QBzEsA#P?TL5iuYG(XdB zn3P)}ck_%bBGT}wstY)xd!Nab~3p zYl^kb}&t~N=p{(tU36n(Ht;7HYV3sIa;$>yJEj{AVuqs%2D7rw|zaw zLIi~ujP_@H)v2}p%@fs_V5?0O33rXC93}5CjJLCk0g{^!HzmSgT`5}SYEb?+Xcbx% zK`LYyCDnTKVkfwGQ| z_og4qA|ht}TDU~sjrtJ*%kV$OJo%XM?G>S$kbbk5Z5H za)M8ZAOxJc_M6XiQW|z0P(~9cBa4DRwZ4AG6>AVK`f(yaV+iO zwF&QJAYzpz3;)dhlaxsx{5ioNwQv2FMeIKdBCNZG7w*ds2Jt@6nG@Nur2d$&PrjqH zH@%hJwHJjx&c3z%LPK20R%8Giw>4rLR_0qVN>|TlQ?aj8KF3j#L6-+EMH=ylgLqH8s%);yoU~-=p&IHdFN$?ZXNLvc4jm4lCW_+!JsJkU1~}|why`)S17U>9|!5J zOU~?R#u)plPR!=XPkW`S<2ES2t?*VsThbRMhCffGF%QbXjkXxjDB8sgp62a2Pi4E> zTN5+z^!yf$R{>hVa?zRS6gUQuXBKuUDs(Tfl;AiIU6n2rmT>66SbVA`>ZaHxS z+mwRjYf{^pv8sk7}T;bfY6!qNp9CBub2t9V&uT#uNIAzsd1He*+t5e zk0k+4Trb~*&i;T3(B(PFYZ&+q2Mpnm#}7YlKDw^Oh(7$-DJNeLGlxf5`zW0z4Ec1p z&;qz`=Y|T9wc1|}7X_E5y6N$GvvD3IVtzK}-3YOFI#YXDC|D8{YC2n;PAm)5O`#^^usOZUtv@qjD~D*rw(rJzqu^*5y&x?d-<$mgD?- z#@7WOvSL}vSZH8o7)_1cnN1w-?VHha(ny^~534MGUYae(YyC8C2D&e(q&=0u9xS~r zWEhew5Sza_4rZ?pld~4>BQnUnk=(x*#69xuuN{iDzfGE#C-ZMsckIrVFq>altj<}Y z-;Qi|BGun*zMYaHXdqxb3e=*K@fk+(Vh!8+~UT!11ES&O=(+}okXu( z9blZkkYdGbPb1DObEoT=@8#v-H48>6ii#gKK9~3W-mW_Un0Cy_R!@82+CeS z3ugm-)4_RB!cN(Km_ezItB+u`wZHC%_r*X38aYnP$4UrinJ%+_c&3`q0NT8kk3 z!XkF}@T$_%t81Mq@D(|V=JdvUcI8K#?SVpv&E+J>`;1%Wt=-wdL-_EyweW16*}~0N zB+Y~O!Pnk(zD5k2@AC&&;f1d}kleMI>2$H%lb1@t3kQicbpl_w7^R<>y;eK%oq@c+ zHGOF-4cVS{PDm?{^cU+oQ_P%7p=dAh!;Lvc@w)UN-_BbM1`H91Z?NN!>CwtPLP|s8 zc2#0+{Igf3@Aq{52;Tua=-jny&$NWc`E%h*3$4$l-?>#7k3e=&FI@p z^!Yy7I15T>m0R_l{YXuDh^yn*y>zXI^WS^+LhMzb_)oDyf&b9+Q0Q?24fFFXUQ4Gy(mjEGg02@? zor>S7ju>WcV4}f?iAp&{PS21MAag;9bA#r|3v_60xicY$2+#RJ3O)@K{&l0|p^i3$ zJ;T2u=keMB+DZtX${4qBzL7P#Bf+^wNi!DQ#@z?_=u798FOK0o>SkDu#-?Lkl4`zv@j(9;j}HDm^B%~y*YW*o9pNlv1q-$zaA^Yz8%*S-(Koi?F`PE zafXtJ>Bpf#N$c{jKtd~68FCIm3=k{^V^%Q&Z(7uI6A9piAbNHIFj0d1)5pLF750$t zk+XPza&K%Jl(1V$?2uyud>1@=sywDdh|7?^yjW1ni?*ic)x@Q;oUeakwvm~aF4?%d zes`|hcYEh9%2lP2iJ)@s%&xPRQ}sKOsZXBc!xkFK&pS6;nHame*K#*b zNhsH=7|NTO^{}#jXn)$daO65BB2&{5iVfg(=UeNA%-acT!l#a$Q&s1ijMmSW8mn=q z3$Ehz@zcq@rLuYaS4Isoia!^>O0Rw4AEFIm&U1fa;@2PhXib%R|HtPv95ajTP^FbS zrFjH&O4YGngu3op>0VmE2Hq;}y~JEu`4M@~`)kgQ3t_WZD-H5=ap*gG%;$F$2WJzR zSCv^yMKMlZlRLWkV&Zz&q|q32$TF(`qQ}1xGjXeXJr05+$B#~_0U)am<~2LWagKI@ z@6Eg+Br|NPw(I?z#QDso+i>b&t>61zTH{MBezL3ct+EB$czgaRCWIN|%j100OvHgB zX7p)IsXBFcN28=$eJB=$kB8F&ekL-oE^8nA#h;U$hz_V;;F8;~XP%U&BI+AlP?C4u zKDp5av2}rxJT?0|*N4)1laclcR1uggax@ii^AdWCZgb7PVjSxqmb=dkK39sma0o(J zp(GwSeFH@1^MeEcgGB69nO03<6b-~&O=Mg^W^7j$xLgp#V zbb$?+8>DD$>{{-wm3@12EF87CC3IO7p0mL^eZ9T)TL>MEQse*Vv?4A>hKRL*W1XEa zAv6^z=oK+1f^lLj;*6+7s4@Fg483Noiv&cNumQr67Uz&bqDSm@a76P7wQ^*iCW^rW z;_IKXfucFMme?t!JY@C+y&j2SjS7bJ(U|UO3;4yXQNhJXDRV&LQI?+q!1BrB$*J6x!Vc}(7sdS77hhbf{1H7=bp;=KdJbp8IGas9-h`1# zuDcrtpr^_!n+g4O>%tGrOQQ{?CZBwlX;aXOWX-P+RI661@uq@fJ9h&wT-B;(yUNpCgX^%kAw%0OVVTb`m%g&3o)|Wy2+o-V^CBa1V+= z?C%s~-Iv^cvspnJzrFXzIc_6=flT5Ccj6>4GkKtycJI{-$kvC)@i;o=g!ALp@I+kv z%4p}No|2lvNfRecCv`E@fYNGd?U%99*nh>)ne_zO7xh>LSpPF@r?W&xczsw(Y(D>a zq6PF0a^~0Xw-C3!Y(lQs-N*fE-(|)LI?-+*=m!~OD3=EBahEgkm>GCG!H}|c#?_xSc;yW~TaaEW%LLf#9jagpq`2h)Hw(smtb66HYW z>%;Kj-QKAlknPdVOfr8Vn6Q1|!T)npwL{SztAV0C!+J07R~(N@5u+|5?bDFSYc9RR z?>1qTW4QR9N9ZSP!-HCJji$H4q{^;mq5eubdDPW`0L*$f+&wxBTQjp2cUOnS z>GM>x%q}Ort^JDWI83^OF*vNM{BU(=m$b>n&EfV_gio;m{mn&(eBQ~;r4)F1lZW^1 zVrn`)f2-*;+}T5|1CqrL?e!%aH_sn%x<+U^fXk}PuP7-fDFqGYl>5f%wXvMn)pOE` zt(!jUUz)nvr!_YMsXHI;XDQWFLn`_P?LYS2)*hGM=y_2M4$3==8tQl1>DKP~1inJz zUOx(2ny0#20U={Y@-Uu0%}_$vuZW~y(6G54H$=Qx*0(q9wvR5YX#A|u%`sA!CzV;| z8LI_sNXw1b|Kz3MxaSu5FhvsKldh8(*{%>2(hsV97%Ceg|Ix<$&uTPCV#7lS)P z`D+xDd^&6QD}wZXnEpqa=?dv6GablKyE;|B4UreyR-9AwO(|zO70@#Z+5ps&`LK7Z z_{-%OiUISVc8a5eUnpcjEvAy+Bwhmzi$D#+MP$$7IkCeIRxM2PfwZDmy)mJG6waz; z?i(og%_M5OJ{7|j&t{C|v^qC?k4Ul;)&Yw=v%nTz3pyGaU9l9S-&vfl#GU7rCig*gZPT8lz+eAXJsL*0iKLfcukOY*9>`P! zsOrKESOYks9jwY1r^;acDrj^ym&CFT&7E7&0-^k_Z@i2mwfly5gAR}MUc~B5V@4-nG>x+Hd$+I3kA`h08gCzNAufeNOy{6^>FYp}c1loy732o+;T*xClZl z6>Hw0yCVE^Q-~Nsa#uJ--j0kFp;!B3>J$QG?@yL+dH?}tk7yw{T8(u;5O zfR()Nw!EMxMji866*pRU6h%Xn`HQ*X;D)DRzZ8cZgQU*S-IeMgbiG>|>tT9z=(*?L z9Vzvix5vT6-bUlP8Rnp=Demx@rI_J&J#+j9#F?LJ;~DW1c`!QWIxsEXq>s}ZsI^=#K6FVN3Pj|h5E{T+T!vt(B{)s^Nw- z$?%+IOi=uV#BvE;;Mv)qoh+OhGZ*=Z><~KFXWo6QHr~5$>9+YrmBA!$C?|dWOk{Q2 z_|e6v%kVET3WxeFQC3@T%Fzh3X2-6gOI%pIOKw`xI6JEZ(4T8{%q>XTiQw^vmCG6FxNW(5?ug%yl2=wWfuv4P(^dz8g}P3S6!fg z$NQ^Q;_xMg>r+(`>q1;FdO8YtckRda(=z-y!3*MsLaK--R=*i+*p}@7A_RryXmNrB z&dr%zKl48?g&A=ICJ=Yp{3;e~)NDCl;4rShSXVj?QT>Km!pke-_-Fjto_m_n#aczf zYn7v&Jj}^mHKAdc-`!W@(#JP4uu_+-4nN=W%{~?#8aZ6c^Jof%v@Jqz_SctNC{#O! zibfsaId{RBEppL1 z0=KOt;)@k8N*j8mEd)NXMm`Wee*##|p9T;fZb3ArzHj%JknRcsSE+{AR}SlLW5jme zxqbLVMX6mzspp{vN5VzDUBU(XhY>H;qZH;Zzt9ah+qCexvE6WfrSHX5d}09KuG^_j zyO|pW!XhBD7!%;GxBjSAnojJLY_ z0Ufr74y_%)1%F@f{gyx9855kFuiX6AV_`t~U|7-F_aGNk9o5VWmykQ()E4??YKc=R z)Jwe671SLN#NE^3SS^R?1836>NmqHjGn~h$PJHOfuU#{2>i5OC%rp-}k_wBI|QC#*FMnMH5S55G*aHjg!FOUI;3U11|NvyvYu2a*w%EBRelwT&_ zPeC~IvB1C`(|Is|x-#tSX@kuo{N3qBX8Jm!KG&BSQ0`il1Vl;?om4+@%YnQvt;DK-Yj^lK%^c-8_aB3nEhLGVUW^oxDIJxmFEdWUPiDKI;or$v*TVl zEr;YXv@IPZ^qTaoJ#nx*u>i&JCvKBNxqn(`C|L9oHnmNd-X?AVtb@CtxV_@Bi1LT* znK4WfBDk*qN*T2%9~#f6CX-Uddeb7B9T6CNq2?>U~Pv%2Q33G+r~v27~8Y8malnNH{Z#+b`={$twe;Sc66JN{Anl1E0xEsn5B;@cwA?r*y10(RhTSi~Nz1^3}jWrd+b{ zwajz&(@g#YGQMQ>d%vt)vDOD#G^tmRgeN9&TSdg=BX2mcE`^X^$~|?vXEE zX@>pSq;~EeU9AiL_bh3pJu-5SmZ}{XRwOIZH(_KteUCpj%4)4#twa_(u5>dYYGENY z#lhNc7sw?#u8EpslqGIc<=Tl6TJ)o_abEDR5JGY$-9a;Cr;2 z2K&ohDjipVdV9ObWHIzn-P&!P5BU&?>9vW9NQVugDzcm!RV#W}bfl+Y%}`;jMa04h z`)QKR<79fpWd43p+n=7BNqZ022;HD z-HJ5;6tUc>Jw=Msgw4gu8H@FIWM77b{9^fTVKXz12Y=MVRZmLKy{A;^_% zdHN2W*XhDofLu`~n783M&bQ7K=N$Q*B?7n-dvdt z=Vr8F%-BeHk$aYIUf{09g_Z~4aLviPVgi=p;@Pa=5^xd3d9#|ibG}VwZgAtP79;3! zkc*trb<$0P=}Up#%tx^}X4p(zxnFCGmy_DYQJ@s{8U`trL`ogk*x68@u-sh4-{|u} zHmI=nMyj)Yk?d5@qE~1_d)BU!HppfTxIj}OlF(Qn^%L9U2y>j4RzwNi(@6el@=I<7 zD&s(Y0D@$%u0gjZ$Z-EbX=(a9cx&1kdi7(&`ia?CNKySr^CMLDUyMD4j>cKl#+E9a zO8>9HNyWMI6y;k0$J*|}eHSY)xmKqS!q&04Gd^RN#9O5_E4P?akzwSX{ z^4WxbrL_m5MYr)v|DlxZV6)K5P@yurs21qB?Ge~>c>4=iIu!G^J*qt)HI`olI0)y6 zT!<5#IOSo{IMrO<=_^6rktEuD#E+oOSlt@1#siUZ=u>|gc*OF?$H z<-*D8#L!Ku$#r2BKUu4F$im0bnf^U8i$EQ2NywKCrg*PENerA6Yz%>6?S2oIbqW31 zbq!Ubjsug1^00c`H*Gc0m~ZdVw+i~F4EZ2O6+>aR&Vq@w2%dB2W5PxQClz zSPR^&(fE_Ik;+t2|A2XGgvqZ(S;?udq?!!J@#S20H<4CNUZ@Q#@c5yq5XtoIq7W2xJ8rJc~sMTTgs!|&Em1vVJ99K#tTfCGtDs3*J zkHf-vu8<1t_fwYop`QDm*zNyloE4$j^zgs6#@2gJd*U;C(wh(JL?Rt?4#p><)cq?o zlQKi%xKGnSV*@V?7K2`-1YJ3(?R<9jrTFQ{Js2qzKL9Yu4LxjFmMqL(?+k1eWSRXb0wC zc3M=v(bC^=u2ON9K_#HmGF-cuRc_JU-sxSpdWPp-+zs6~I#ov`_$#z@2HJQEMo36xagAziZzdEIt!zaWpM0 z@P2V?%aq{mk0!}rl`b>+wX#j!JK$GM@sT?3qc|0FZ;f1%W1+ycq~gnzv3#kpICi}& z-jDhY{0TDI-EQC_O|!zi>vM(IVFZE~s0$D&GtJ8F0^6t1lc|m*a@22V0)EXK$Py=; z$#hln57U(g07o(S(O&Bh>#@FWEg0`eJB8~V=m836fH{%>Fw`HA8f%AF(EaAkx;So^ zAnN%O{ohaBZ9UbpmJWYZR#k?~X?yz5A613bOM`d{ULY?f)vve>^9kL{o zSeaSNaZiLZVr}-Dc?F+HBb@Pe(ryghuo3cKS=c_Ui)g!G zC$s=zfy3WGsSGz4bo2B~_ti7&lwDk*{Ud(Wmh8tMlt5%m!$cnDa)M1O;UO?&h442f+{pzA!Zx;^gLQSpbE7hDQ+PDcWJ+Goy z-dK7V891NVeh|#!M*ri?>Fpb?14r`0dgcm&WZtJh5jG*qY7%&u{5N8OZ#Mf4z7=!% zXO7u@JbO16$Fsvc4*!EjbT&#;EyLDd7PR;SB4ln_~6V*tsOcx$4k97Df4$Sv| z`s_NJpc8)k8nOcyOBeS&5yFh)hhPpr{5bSrWQGX`E`6m z|Ch-fY2D~KR@R%Vuwe^=$Kt#5;D4F*aXdQ9wg zZGWC=h7J$HXWAMEP1V>(+a>lc=gaYD*hLaKOePa>Fp}@m$}PS<>{INYKY6dBKUmK1 z0tRz9fvmPeZ%!P25;il$XwP8oCn)a|B}Lrtk!DDs$I~#xP4(XTxNVqgJ54C%&(GpE%Rae&~eGb^W0B|I@)>sm$lXJ^VXKM zc8NPnAz;4myL&M+QhNfCqN6;L}V${E{w{dWC>6G@}wxz#d?rf6$Lf75G zW-~^5LRHnh{$;t@g#OT||B@*vk?mGVlMYpSfAj*J(T{XV9E^=FmclOYN#K9_Y-k)Z zxn2|EF>Yq%aatuO$c_0glZ6w%MN($#6PVNx?AVp3h!lxzGs;01YVx*eOR#$yUl>ocP1Cv`LzGu`f{wm1*pMWHG z6x$bJX4*9s#m2u%@f!BBb#iZtZOo$y4OY+79nYT4o}2PKrA>%km6BcRJ$FmmhJ$)$ zU%`H{KP&zt-Q(Yw?5gCIV~tWr0r%98R4}Fd3UtHE?lV%_B=dWzH@tQ8A@x+Nwjpfb zhxPV>;rkPZl3H-*FnyWu;To|OA4ncl5Dli8lnzmBFA`DpQ3HFhuCE71nU zs#X6^DQuI`fQqHj)SO5Og{FDP>YVS2eIKmiiIdGgm3bL7outko4@OP} z{TQU>#2vlhFZQ5^x|{b4Q|WKy15Vk;QM?$|N+zg_MFnl)0EF2*J`6nIqUK&lk+SuR zc)#RQJn_xg)DB%M2mA(4bIieTomC!x8W|ZM8XCerY}OQVIzJfSZySCxT{>9rgnr%z zcLH})V{&>HmKt(Gwj=FDqe}Q8LGqM-lNs3=vdqt1P&>)D-g&4A9U_$N+QQryrAe-u z2XLKRifTod0yITC*0s0c$ICGTP1}BBBAs1zf1qh;e_kBUY8y|a3m=O^YWCV7G~KEP za&2TfBlhi)10ewiFUy>~n>i?Zz!Y0)n_`jafBNZ+25@n4xBs4CJ5^XsyYgIHlLbOn zOR&T>deq{6so658$ZgJ5_rfSV2hEv}V*)2XmKJSrJoe+{_!5vEIjqkzIf?t8ke2Gl zpB*96**h4=kLcX4qPT6^>mD)A`kA<+X!me@|0gJ#GyAg5G{>uR#Du0ALmzHl67(XUVI3VjTQJik`ozP;y$0DF~zYc z2`7LA{gk2aH)HPyr00pyb^g-1{95Ke*=`%@bEVQ3wkv3{SXD#d@{S=O3(q%wJbO+5&!;h%DB5Si@bI9DWq)A=-wIVXpI@ zYnndzU!q8e^$C71_UQegLld*Y#l-Lk<@}?Uep{(%PM@hbb4JeT?#Uz5(uW_!TnM_j zl2QWwCw;+f)!LPz`hv~+Wzr!YHF;A)J;dLnC_O9-hMd_jiCDov0NkmrS*#~uQ zz;7y#Ds$AXcra&iHF@#Id88~7>!>7HQu&2ieU>i@mTo)R8JFq(_!^RNT<#ylKW%5} zf7e-OBD(ZMgP$DIgVP;3^c}7@v=p&2IJ8-|Tcv4{c(v^f<~Fr7^&ES5ehHD|Q^RqK zXW&gvNT*>II6LkEB@A7pYZdBJ#I4_n#3RrWdak7Kd8hDt)xV(8&pDG39R41n_^9mM zXE!KB7n+a0#-em^M0J*dhOKOm;en;WdC8-p=2x_{khz~bRdCY#N(Mtr=aE4qg}O`V zbY<`PY;%@kDELag?vw33S@@K|6QDtC0QdP>5j`iGPk#;JOAg}bpAM&F0`&6?22hJq z#(a7AEJr1RrJ;xUYA=l7-ZNcN`t}sY;_2);HRk)OnSs} zMrz{eJwWXk5sZ#J+QJoO#>_wT^UBfR$xW}58>^Jf5DJk} z`J9PgkK6srb}g{7npCBAuOSys#ZK-Z{CmbyD~^CwM;~rlpRSE7oo-WoID8+hxpC{FDdigKDI?%C zx!rj!N9&*AQ_9?NrRmq9MOhXn;y-R?$vlqW{i`|d5A6A8_?KB$>4(x=e)EAtY2o4E`0!5RX4{1PtbfseGJ^_) z*<%H&JHr?mHOJ*`CnMyse!RX1rmHDBVk7KqHdv(uGro37U48D-!w$Wn+EL8-xX9Cj z7`!+v#3!$=+iZG7OGm#4x@|A_v-F7@G2F@Z@Zn+ue(&c=dt69YB9}#SB2Y64UILrv z_|W>LD><45mY^Cje5KYIVh56hSMe%1jA(ASMq z-X>|xHuik-Ul;ctZ*mv@^bYg*&0#q1_vBOvpW_vxVuLrAP60T7{e%LWhM^7yy?KCF zw&%86%+?)ESd^b(%V%5^+_j!4Y`qXupUPN)o$z{d>l|K9R+Zy=;6Kd3Q?4Upx}YuO zEkk59?YLB07Cqp`WQ%$JRUG*ARE+s}56BIKO6x1tXD`;?9ef{N-7ac}P-fZ(#~f2I ztY#u=Z`) zxw?h-r`qn!As^?spsHAnHcOXgO$Gc+n%hk7y(Yk#kXeqHvVU!A#K~@QUTcI4voP|< zm6?}53jSf&hir6>|?Lw z?Q9^vI1(7E5H+On?FPfE<56tFk{x+o7GPnB4u5OTdbno->b=K{Ra_$3sIGdz3IyYF3=#%|v4_8no{JudTUnR^sYT+~zy1N?0# zSAVo$N}6#N!dY)kAUssBeLHT2!(<2Ys}6|5+iBjnoGd7IdY*yyf?wEb71)_yc-A6} zX-V$ec;CcS{8FG6_Jh0Y|< z`pYjoCH+_h?()Oy5)d_^xVs~gYRC~X`NXF0TP8q$QSDrhFnpv)tz>y7dp)9!cNLG< z=00HOs&SXtnmbSjb%?T2qOSDG(w`^9nA~xbH$F(|!#9Fn$;g&U@6}@}SAH-2OC8(UB+jee&21%@{m#<~FAI0XC3a z150Qw+fD7jE~F4X&)mH5I1ayj-DI80!zqtm<6?D=3o_8b27H==G*(82@;@7X0)_L~;T8u1%_$ z*)tsRnLsU}IxqW(40~efjvU`5SDt_2MoJ;!mtj_r525f)j#>OO4nQ1Q5 zSNXQ>#8Fhuujx)8lbUaC^Yptk$QC7oJLeQa-GR&jul%UQ*nc+KsJZLy(-l_wR#xxE z{(fmAlgZ1|>)G{g49!ubiLaTcP@c&3QB`pX%snch#u}PZdAWW4BCoQha>cXVib{1#EnPjxVfCTeKBOxArI}K|Qlm z#1rfLUc{kKWbPHXVMGdKTs}0J{$eH1bs1u-A@uGAzHLL2;51h)O39IU@?c^>Nh|g} zJN{0$9`Q|%ZTZK=eD~GCljM)R@mL%22bgQQItz#X_J;Je+47dgTHn^Ej8>w5y&r z>>hlx4GDv%F6oE;gUDRDUKI^0^3W-h{w9IGI_C~=Jfh59iqNOYD$LEE8rJL`jgmz8 znLY)iB(!QHzkkM(19E`Hx#C5TomzVxdz!diFog^R0%h?5^k}%+B)e7fAo%qUJb`6J z3<~G<3%4k~@W^q-EBm^K+!!1uhN2xhIf+?U`)|e90K$1X8P%LCrVr?!mkam2SY|Ad z&%b>_Lf0wLeZTvCM<)ad$##?a@S*f8ba%Z@dMp3NfqJ`CIeuxTjydlMTDB6E09_Owc(*90; zYGQQ-N8hMCM;zOxO^hB(^{2{Mq&fi|;d4E5#vTt90D=7m4#L^NF}P#)!qU9NwzGV4 zH~FpjY@rUW3WfmrG|9g3YYN*9DnLznQTb9OHv`PnB(i%8W4)PgiYk6Jdm#4=v_79( zzRyrqvLEMCO}>1sEdJriuT6qE3aC9~evt2TME|1F$A#LE{?|^t4rj1SA&^>qf=#() zT@)XjlF9IqZRi>|s65-GA2G@sRP#hRZc%eaHo8Y6W2sM-;?2`&MR)WYE-O<4SoJaNu1Fzzd|o`jo>;ds!>XcbJv8MGOreUPF)K z&}`AE9Qt8HZD{0J{}47-jGP^}o4Wbq{I`i=tj9m^nRsjS3ai}`X@hNr@pEC7dL`WP;Bu&-2;pHW#&vel z@8lcTOH2hd*$4(JB6q4wQxn`>{O;sUTA6l3`qmL^SvTzc{-t~|Jmrp_Xa7fgv|Oz+ zS3Sk@UG(oKFY(uN?qqGoG3yZDGU&k?b%Ov?)WQax3b?fSG21PnQaWZnB59=G4@TlX zY1I@48w6!PTX)fbagY0z?U0)Y9A6`at*>pPUQUAyx%Hc5jxxs8mm`jxh}s#;_QVL3 z!S^k{s?N2uw}gq8rA1=7$)RwC z4~qN&$fFtV`iYE#SN%E`0M#*uB-}xO;jw$wvzeJ_t!kDk1?U`>FGNP$?PA?a2}|hL zM_urBCk zE?@xo=qzlxU-Qn7GW%ZY$Z|XG4BXn*b|w7$q+2V*)pE1xb6Vniq#k**NV=}{7PyG| zy!xs<#?yN9A*a7#u?KEM7?Jk_Q*R;UM^5+5Jv9A0b=FP#*7$OtanWH56V5-r+08;0 zx5>*%b*bPiH$}Ie-|+~!p<6k8+};sbfdJo@XXvj-u)fl5=S1jU4L#2anqgB zT0MycwInv z+XXTK-A(;dE^a?5c8fIug&XX)p@T9io2Y}w(v>_V;*5j#?AjUFSJ$MGKG-|?uSD#j z9P5~CVrk^yZ>e$9Um$dz`Tkb5Xx2&@6T3 zZrWTE?S5exU14}2@){}bO#N^P$bRS;+1ax&UOL9_L7AO`^#L4gMF5-AoI<*KODv*E+1-rBHpT_^?+*>mpnvAHH<5`I@ojZR zbCwHV2*_mpklgn;#sVTfq38zwobQE;_o>#j zgiHoiS8TmC9!_DwgMUj}BqSvM1WN{vD|JtN=twEuT~E^&$8OscGNIlQCE-69niLQi ztXx`IZnhY9QAg=QevP34q+{_+6yQxRYwbtcIy-BM4xo9ig9s+dS(QWLhTUbN;LIB1 zrZvl#_EZj5P(B;MoN>epx$m_y4Wl;cL71A?sx3_n+6l zg}d^oUNbdG)9HaJIGZPx*q8iXi#FZhmrdr+V}W4Cse)a}664&VMYCaH@bY_Q5OLz# zEuSMh)!;R+jJaRsZB)XxM=IiDfdV6L(2VgX2ybw$MgFd&_s0X2g5a|dy7MX+IBvS) zXa#1Zk?BspWbngvmfL%i?`;E7?)%rXdMiB^M~$~!OTroo3s}IzIxeOT9{;Wze1?*u z5vQ{dlP@Hb3VJFl@+U2$p!VRPHTKmV;^Mto0A9KU-UaY@nq^b%Gi@xFCyS&SU0-;} zP3$Xef!2=xhMJ~(I@X2r={~;iG!;Nu$U8Da%_`F|6~}Eil7V-{=LY=TYNC<59u3Bc zFq>=o7RmEQ1DmX_B;FIrm-aUfN$oixReYIP_KGRND=POi9DAdlWFGVB%ZxP+d3<}d zgdWpX6BK;=PO#De>RN~)0s%<;Rg-ii8RH zNQmz5iQupx$aCFF#-4?#ih}w544t5?o1&asX5TA$%ByC~zI5<6{N{y-fusV{za|R` z#C!dgE@@dZO;z;W&(28rA)7sua&)HCBHyGe%7L#EkY> z3Y*GdKhX$|#a&%D}&4ev7}};W{$KV?kfamu*x-VTGxqFI~)%DGJx10fXXZ+OPc`*`~j~ zW(0=cFCzwPWFqm$JZ8VTG}{TjP4YJXc2$_#9`eh2edHL;fnL9}~1nnoVK;fJIhdA~p=N{f81y68`bua@3zrdwe*0e4yn< z!i{|xdv%33MkXdf{ZbGw`drld?yA|vFuyGRR$kBGQ-59&pyVrwG~2PpGE)*)j!?ee zXI>`v$0`tFz5oDy-p_nneImyD9_%8qbjj1uQS(~818FL^9L>&|VR7D5U6&BuApH}I z#p!T@#V>T8w${x7}=}Vl|d#z@AMJ916RwSR^ab=aeoV(ie5Ba4(-t{T} zA5G^T&jkPe|4Lci$|{v|THO_O3rWmz%iTfcu2yBT${CKE%JxYJ&Qqwu@f|9jBxnMJ?TSTku|vXT&Yz)z;77)QW+HLFU>Bc&NDzVj`H2%#StzpX+h&G@* zD1%BOZ?h;&SOJZFoBHBtHdM;}pH_L#=0xO%Yjy$&2ANIhO}vra zI%B=@I6E-9v)j45(Xz@x9Z~v7(VcRSzVM$NG!=pF4nNqX?FRqJDT8uf^-JHUkn;(!LOhWCXdfg=}(sDHbo8&U(_>n?VUA0=$rE@ zFtHRDDVWo+W4(z4uD?|YTWX(fL7KP1MV&_+xM{6?9y#D@D0X*1 zc4i8SG7e*>AptwC8;m+v0b?8oNj2GS8iH^4-UlwUf;5DUKrig*w}(8vxw3~9QPEHl z$7M&1FZSnlTC~DvM5LXa9pO^9hWKHt^laN}c{M5hFDy>htD~mT=ihY|CFz;RGH-It z&&Uzk%2Cjo*x!i>x|73cKl#;hDn-~37uvaaRAT9ym8VW#&-~s4TU}sL9=!)vpPERE z@uP!77HO>W5hrtZo|f_6^cpMkE%{@ZkKl^V(rOQj&*XIISAvaQG9kkKPHfeZ-ts>8 zT5Thcvh#yyN;2!MV40=bA5H~6k3AIiy&q*ahsQB&g?6n$d#-L9#78C#22~6?1WciK zy{J4 zF%sU8$-{>R>$@n2tphcxeG`|;qSbSKH3p?WcsKq75>@CJ7}@vr!z2E#%}st6#ES$g z3T6ny9nG2Im6uR=w%$&uMp4G284Bb+M1^MBWut9b+p-I2t1?}=lP_SMC-pUjEd>bk zU={kr6qO;u&QeiHNW{iw1(qaOXUvK+kti&fSF|%H+(}(a&Jg`i=UxhqDpHZ=Rr7cg zI(u)HD1W|@^v5V$HIA4$-F6{y_hrUNfi-|gmkTKb0$DBdt*K1-Cje1|6f z+or7?nY_yPi5nkY zV*<#l#aZ9cX?YOi^&dmx7ejJNp%9lCv5;Hy_^U8C`Lg?WKJy-}@z%16-S%OaxTjrk zhPW%BpoqJ!Nm|l-wgfIbo*-sTe$@_m(?n}}5L7b+39}cRP6YDR(R%jiXQEGEVFX7T zU)N?`pS47><2>9)@2w=1+48tq`<4ul#*B}`7)!7$ z@cQ>uFNwxY?flpJK5^f&>+F-7w1mZp!D${i-9G<4A{o0~MSYf+)XM@U}& zq4mJ&&kj_WibSdOz>7|O>+8J($LfmZN?mmQM!?P)->=IOpRFd|xx$Ob1e81eco^uh zt129~W{W0=#KS-$ey+#YLdiZ>d9*!8XG4reTBMvWm6BW7flf*w z7Q^&h$Os@&XS>&0=x3ooGv;J@{G#AME5(l&25kbnTOB+Z9y3Le|`>Mls8(u8TvMI0`q9o?2)zxr*O$dMW{tIam{xQY)9VmYJJGAQ)?Om?J2vgikD6z>aM7Kc7U&-QYE{?eheU`$k; zx&>1}Q4Nz&v3W@;nWvr0%@u1zFhQ4j(=lYAFC3{+BwCBw5iNE9&ni>-vhkRq_G?0i zJnAQ;rt|f+z};WNl2ly&9W$JF`>gY-(?ox5ctC)S>drp*1^*DjOg<@0%01+~5NBda zs*K^8w9aTt&i+TRH*CQegM$TwmHSnIL{zx!3{`Jp?n_djPYtgvBWS8~(~@V?W2>;V z-e|vmN4?Vp^}frJ7gUZKIrU9F=)3hmaZQCrSy7kVuc8S7;Xs`b8g=S?=E9|sJ5UNd zG#a_kp*}j$Z6o@P*l|;A06beWv+FJEbx{R9t;p#xttdzI%)4n^!+{~#0#U+&HCdCY z+KQ3SB*@4JxpMuAr#W$L%m4J2xBT?lk)5|VEFpkm9LVQgo;y?D{##a5@loNF-kN9f zcD3odxvcc0CiXe4)}R<;YR#3M7WcoxYUWecna66Q;zjbB^~G>X?=7b4I7!9#`W8cHJ0TI}9O zi>*>GG)TIllpocBpWbU%f_36tY;`$!aM%VothC^V+qY|13|sj@jd!C;v!;$)gs%U1$jr%W1y}*!rph_%;I|`-SHHb} z*K51zu1F}BRcwek3bip@^f)`VaaR8KAoj=bR(7V>QPzOP_HwV_UqG|VrMPy2$hK^- zd{f%p;GyoxzEhxj2QITMpkghNE}H`J6mvq`NX#NgUA^=$HG7WnkF4m7+W5#hSfwe^ zewKuxw-W^6wNmlfI7g1{rk6$6a(bc_vJ8_V-b$Gmgq1Hee)Qs6x%iHLhtXz@qi~{x z&w7(hKL8-zT(naa7Na8JJ7Rz9vbC)6c6GWz)4thJZuyLznbybk&(7$z<3(d;#1Ruh zwWjFs?Mj_`CX>n&_-liyRumyG$DXQn{J)t=`;@8@0cCE z{$o!Pj>MzYEJRf3-*M*O*!cmgV^7WHB5+7bsip&zm>jWw%-C@#9e*|$xibCV+1ap; zp|C^CU9S`#pp4AQRE{m$woZXYdw6#OdC&!qofUjEo|gO;1TvyJvYJ`I6IE~wYj!Hv zS%eW78L-Ukc%~-Xu_$hZMo|2lC+6`l^W&es{paM{r&@ zV)L}|D&|c?m<5pku`E!N2|Fz{*O3{PspDxT6ii?Dd7sMm7-4 zS_b6i1Cp8WP z=@rmd1Vu&CLeoWqzh}tO_7yq8Ao}%00K~wi%9_wK8$ndXKqb0qhKo}PoORVz_40Wo zgoZIpN3@CvOHG+hNZc}ge=@K3k;|sbm#Gfg7o#$mO$e~G^jo#w;&l7Uwx|}~QgI?f zTvfqZ!x$b8IjF+YL0cE6vSs<`qorV;>&4WGMjQJCH;*Qt=jly)=B-ObcL*}SECQGd zXTE}5{+0QiN$PJcC-fXgzH5PEb=h_YpI+Ruse5a5&9uqo+i=-e?l}zncy5EX3sSqMQ3`MQ;qR9m zg~*a#2KXhIf^0+BHV+ql*KgrV<$97A|4v_p#l{$-DsM_La{p@k5R!#mgGiLa? zu~z?C0sy@jcD61Y$U24(Xend37)i)ZbjjKWn;|hA&;IKxEiFtyG~1u0erZg0-f`w9EY_S-m3u`Zm>^6pU+`MI)c=ARgB&q##S`fRjc zf4Ph04BE*g`5lAt-8Zs}Paq*Dw_mA&L+VZ(Xk+N;V)8@Ysl@Gwk(R=@m?PwiVg1|p zsZR&qj@w7qr*~C{cAP9FD}#O*1pUEWoroX8!5|V_659V6$ychB*_5gLw(E25zS+MA z{Q^rHcz)erR(yYn-bn^5Z<}l}0CZ48?8q6oGWO;6gh1#vv%-zB2tKE@x$#(wCDa-L;4c0BE=RLjS z1rQAv|A%&C&5}@`wP_rDG3}m`^NO<29y*dV*V3{KJes$}&Iat$AcXt70~?&Y@**5x zKSI#9Z#~ua_VUom!a!b~ntKp!7PXiDyz0)iCg`V^=T^`BVn|N5N#%4uO=`9IX?|G& zKgIO9BWJ|MV}M6m1Yf`+k3Jc*TAnSHd^2%lAjcdOOsw;}P&r#3G-?(SpHlph5CjeA zRJxZZxZJ8P-rirErKS(L%J*;5DJ~6Tc~S-DGQ*b}Z8f|p??e~BWhPV_+ntqO*-9Pk zolw&9&v5~oa27fh61BGq-dsO(Ie@aNG2!v1sWr3SwcS+(&i|DiZS`*B@qb;p&)P{R zF0`cLqgs@a3^H-{zLw1JuWY3kzD7Q(-9a7tO9t2CmnV{TniJ#7n`OzPl{q2t^N#Ww zM*S@x96YaP3Z}EErk6rFpv#*ofu5j`5#<%GsT%|JG`pooNDA{h<1@dT{vABl>fwUv z?|DmxiNu1KX!j2utfqo>OHR}#a!~Xixf`dG(QhCuv#5oQRPxjy2vd*2!q9KQ<4Wf& zE)pVo_VgiTvM&J6c`LgwG@mWzs=k~_NEPt)R_;mXs5ym*x7CeFsXmQBM{#8gqMirGWsH~Vj0Z@Fr zpaHLB*7T7G5w$Ztr>;%Zrzdf+1Ml*sx_De-((2QM18C65GsqVstEk+yk^7N@C&KcY zTt}%hDyu?1Ff9IOSeYypVLzCtx7>9ubL+K0fBS!c>c8>Vz>>OBR-do6P}`iR*{1G- zXVW8zPp&W zE~wn?D67b|F_I2hD=-R`O&#^fr>_y7szQB-V&uw44BLG+?|W@|_m*{c4t!Fdw*hnm zYb%*|i+FpUyE-L-d;PYO=Y&!?SEnp0`g_cFdx1j8n|mtimp7~-9m#sJDx;1u1c=5`S?JlX(j_QaBW4aT zezsI^Fpz0y({reB;a@ad_c#cnlcD&owsOO`r*MsX$`#_8>3?yq!T5n^P`Jl9cxQgj zJ$%J9wQ^9FV7%#gsYT$hamHD@Xdg>9kRHqZ4`KnmJ%iiot_6s%j^*o@*1e>upe5s; zKt`|9narm_;%x_!iL1S-=GXaSRd>%Rag{L(@eP@`r&J1FCids{$KE1AK^?aO`pAv~ zOAxW%CtzOVwKvv6Z|9e}k@u2?z38VEffTeYrvbM!^C0P5fY0VuC(7u>I@(!essg*u zgmL>QO?yL#3$1YTQ}C(H=UF^$PB&@Ow*Ho^-dKKOt@GJb_cs}QBQtjQUip!MmOvZ3 zIqtk~F{~2aP~o*4sg%C1x;^JN{;I{gW`QcR?flDF1MWQLM+aD9aS)wxHcdxg&kz02e5|qqla@$J0ZnD6Cot9lOR<(201Uq)W& z(m1BLBJ+4g0z{LgX}5P3Z{F`V94$44Vqaqj>d3&g%hh&!% zYEX*6U&-(W(w)`@QX6mI~<%bnR?dpPPVU z1J5w@4$Z3O6x-R146GXOu}~icEf-Cd)H6Q$u!n{_BG8`o`&tr!8r<(RbQUH`}@TjAOGSyXficoA-QgT z@t1j64DFn5yzE24(k`7wg`p_2O6Z@Uj`H@ZCkAZU>pHlER+y+DXnoDkoo5ONm4QQ? z|1%#i`lR9Ilco}SbI?4y8t=0*i|Qlfku^jC;hjKk7S99Y3Ad0;Yh~P%D%*@z99=># z%L~6-(}O6{q20&Tg??klON>7%706!Ti~er8?l;k_BMcAyBR3e;38LfnVIIqtL2DX+ z)pGwNkwBfvrhU}(WO*=x=aWH&(UBH07eiwavjdRj#{h+OiVo~nz!z=ueOdZSWg&GtI4BKW9|25 z88$+VdA=Ms*a*oz7ua*Jv_Vrf?Mgx;V7KEiPww5pmT)>iXQ&+&-!@}{R#5YG``OK zDB9DHX$H)gVjFL@Cw78i_jNwrfx*t(rh8^pVoF`8iFqxaj~ zmLYxz3zzB7#Ra2kvi^g!%czZXe|N|aMWq6NPLLzEDu?MZ%B5a^i48uyD5oJeM*kI( zsfTq+SOz2GpaHAKzs4SdyJey40LV@mqo7<=4~Rb6`|LlnhNoSZ&-ZRh_+eai;{6(P z0Z_xtHCLs-Jq*2dlsw}PLaoLBhRY~~mhqnod+a^E@>aMbf!aOl3Jyo#+_J$I2o`k} zi@s)T%wJ|49yo*0Xff?<&7WH6xBn6AIz5~hVI*+uvKiZOxi%i5hufrh`+Ka=DLq`= z*%RR&-oR>WUQTQ~lr`sH)#`W<7#yGF0%W9(Wru+{JZj#qJ9g)PI*+Y$KfP{^-!KmtI`~-fJ4}i2^3)>i z4RKnx9|!2A;lI1Z9OUUVB~>h>SD?ei^X!x`_7~*BKf%p8v!K(F$qMOTKAV1v%4KIo zGOve#f2nd0XLFsUjUJAi9>g3&Ur9bDTEM4tovp(Ho0DfYD9KSSS=x6aiD}xc>Zp}H z?#Ju3)9trE#10&~jPb>!gSPh-_$F*l_nEZ{#m^)<$DjSvHsaN$>uLDzxzjqg9>W7?k-C~ zDj#yA4QSBJW|{&P&tIN`T`3@)^?CS7TmkbJ>9{>QE?Sa0-AQ^fF%`VX|6Q6gjCf*R zuj_T&rzO^>6!{*BiUsB`)m|+u+)A1_ zcE0~%r!wz=Cwul*KAWtovV&M6+ea(5#VGh)!22}8QtzO9AhcRH{34?`<#*rb)H0Y` zApwO|>a5_oTi9_*m8pSgL+!M>jaT`?jzgc2=hh}%kfSjd(YzE8x~^y+c;mc!u3`S9vSGIS1=vX875RUw2*Wben6o5rACwtd+iaS zkT_Y8O{ZshS*A)*zwK!}CWzh|@xM#adevFgAj%JkCWC2Xf+qmPZ@3S?6CB1{!i90^ z3>S?lpuq;?1dTUu7tH}4)f@X&n!h*4|v0WSZ@yim zA-Z}q|G;<09;~;0jKY%|Zqr`6IZgNPi)y`v)Ot;TCst?{gya?u4J5}f8qzz;<&*v` zefU{>6=7t5U)}kVAOm$l8f9me7;s0^KC~Jwsh?l|9pAs;=k8WWxu)Gm__D(J^b1yv z*m-iwRcT3OmQv?uWVZO+)GF|4fEC_dxQdvZTtW*P#6WgWB`;b6doM^Td zh(hEM+z$STAI^=qg)jWXs|FySz2`d#lzoc za@B9nMszEjaPgVa0<8xexGbbo^?OW$l2Zn%M2M8yX>`Hz_u%`H?i7VlDnn$gzV)oV z(csPZeKdEuCO}KzD{2rN9ox-qwyj%>Enm6G_!jEaF|Iu{$1Op}x!|@^u-{C4etZ?& z38NcUtMH;82dUk7cxby$()?|C3hs-Hr@L!1XIT$cXwCE zctA&gC)1nk(~wyBpG-D13?j0s^sL7ObA57}LUz-XCbaz+*~osqFq3K58AjH)^M9v3 z|Nii5aaqz)S8{=#UHb38mEZL~NIhPk(vBNCx$_|kA8%x6{#UT90u6%-Zz*E;?gqCq z&r5NqKdTUq@5&h6B|MhTk8RXTwX9L!2gi1NuY21%to@_~W&zPSl|vGw)4hImpQ)*u zJGqkZm`~!D_x)&}*7PENF3?x^xA#C{2i(x%`ktP$$l`O+Ly(m5No~ytp#6y zaYJrjM0wyBn}^J_;{`K^yXx^J5R5NPWNnwOY1 zO>qc@$4#Ry7{4o>kds9N`zfD-djTjfXXuEjTi#dZ#sZRe7_ZIz8&H%HPdpAchfQpR zBDPaZNoQGG%dAUF_NGHr&QACWm(JvEgjeJ*_Zf{kwi3_JGdU}6*w zm6<6PAbXW!WOIv09~fZoH{1IMd8((8taKEU|EwR$hs?=`lnk zG_{V{WvJ7w^!K?noi=sil0|Bg`l+Qikq!VVCfyg!pWoxRYioXe!1_NJTE2hE2?%!M zGY{_cQT1e%e%kx0tv0`PS#9xmE8ftWTwMrrGF-45x9)E@^0zA}F%a4buhB~fYx-dU zg%qnET}Vk`3%V}(tyU!l8AqjAhq25qSTEmgl2s>OL@(2fBxZ{+7I$?0{HX%}eosgD z+_6!(f37SXA<^SV$PnDMxiV-p`lT|?+MvBh$H?2Gy3)*HevO%4f)+kYY-%zou2ML* z)H!>I)`3V@TArCQxf;{}je3LaDx%3gMIv{W?)eU-=$yrId(-c?LplHGhCYVvAAHyZ zr;39Qj2wMS)q(?aI)3E_bNg1R-I*1a%%Ci}N-sG?Q*+UY7#TG~$Da#&Pggm5n;jct zHb8?Z19_9WWTAk`2`@!1$Kz|(r@LWc{C>}<)5FtuFXxP;-KbN$W-*2IRh7OXGj~U8 zj*iO|ExAV~c%`q*tW{6;FE84OSf+3@=GkOvevXn;#blXh3%gGO>OTeOC!J+5v}#%T zborj-kwnwrkMerp6ZChLqVqS04cFZG$*Yvxo9|cW)w%V60Y7tL`{1;m2~OGCQ!^W@nTJR3L;h^SQ}wG16`xIUdwrJ$w7+>_;R_)OM9`h0HxsDfmi&(mohq-1KSTmiPc%@?^Ch>7oLT$sD)=com#vi~7Z_Pj!J)CH@K=~CdPuj_(yZm_B?~8! zMd)Vl%ANwty8bv+fxto-HT9$@7!|}?%k?BTuBN$QuC(V^;xoWv3O;#a^x(NbWv)?^ z@tLeQ(_U>sry>FEI!|K9+teOPfdS?0)$!q}-bjIKI@W3ZMUzC)y#2`Q$|v;2SUsZO zZM6-)n& zHwnV;QQd2N)vSnTY>xNZE;#!vgR3|DM45c|`dn@3nG4ElkA^bs1**&HSK1w^wJuqP zU_#4g?nm#1M@DHT@fhI@D=)&iSrn`LyeH|gwunpcwTzmu z1T_oc@r2miVukOa2sCl&8bHgUs$O#^XJTf4`PIv9H^bR-pOB4c%vIrBIOZvyG`$rY z8{+$^a_Be=3#nY?vF_Vd?<*a2zJ#csDzz9W(U)Yj7|9m!s>yZdt`CLItkI5p#h?Zs z>u==gA47($Ew!IR7pe_0|H)mtX)2;3g?GbAxB23Oig%HHgnEqY+f%|!Cwh|3o+*yE zeRt{wzDp;kT|O4IcK#-)j5Q{>G}wEqj&)uobwrtf6;dP>4xc*}vE7eC{rv|c^KVhq zo(AQ5*F5n^RDM7}f8u%kc_+0IErDeIJfsuu$=)&*Kb9NJWlvElKpQc7eDMT0E$V_;ou%}i+6h~Gt?B7siRY%rI@0X81% z-qDxoy#RqVZ2Gu7Eyhjbbt+C9i5A$5EuV5#pHA1iW!Z1L@vNZwM_>?YFilxrEz}lc zx%$N@kU-**lLxJeXW*O8W45ss74wPE8bd#6V6G=&AlU_i;e91E_#h3js8$nMQVAiJ zLnjHTq)@2?nx`&Gt`&-$d?LVlmQNBTqsV76H_l6jB2ke5&o=7h+Ix5lGcG4-Z$KNb z`THZ+_8cnym_XQdfhU*&L29^*<}w-0Z#b4w^@7@q9f^8YXJ;)7(@@-@zoxBJOD!g^ z@rGkSWF?~Er|N;cy2VmWq@Q0~=w^nwlkYsr=ot_@O6GVln#%AROJD6s^{1)4FZ=pQ z-aO6cZ00O7pxuHn>9CgN58Zs_lE=?;;OU&N#QfPfTx~bt*bYkBje6>pMsUAn{y@bw zi$|3=sm(L+t8^?`#$G{a)B2)G+VsG^b3z=68ME^MUw?<7xd}}TN!8(5Y~~tf8p&hk za*mu%N0t7 zMB#S6o^Ly;=6AMEgzZ=!t=c&^V{XOfI`2QV=xus^aZ+NW6a zjGk^|fng$Kb}c{;Q`XoRedaW1_&4u4J^9*UWCfiGYq>?njrw8l7AQpc{++CJ%#%m~ z@a>0NicSqgVyZ6Pa0>3LX|UbU0v_F824=b42C|E z5DVE{JCjC~YN#%;17#>7R;unO3Ms8l!i8GyN{;a@$W#M65*zMPeqMHlm53uA5VuSp zNDkcEaFhZYANqEr05OX&YvJDmHU3Z)c&ZsZ>u-KNYTas?i`{DPs*Gr%Xrg0Txo7P~ zX12PTQxfg0SIAg;h9}m%dVZw&dYF#FjT((xc+GP=eYUKc8B;q^M<6f7Cy9j8%M=WW zgj)9|@}SifmDC|hW$+%9bXsY?c8-+2Z#^S>nA<%xQpA|nQYTJRdN3zDAED1>%pNb; z^hEluCQ;8Skb>ES!v0)`<*&$%e(=$ksCQ?C2d{){UWnJbc+YB7`CB&Fo*rvjs~kuAhi+j}95x(~pOCCPtm z2v052widp2cc1LyYZeYpIe7J~{}AsLMS}8!3Bj3sPf2xP$HHixnZ84d%#Y3JsLrU1 zWG}AY4Irt4RsP>5BT{`)niO?U)3y>bDA)P5f*Vt-V~6o>Hqkh(k-HX@*qMt!SD#8j z8PvZ754r!&Jd_s5J-|$<3fgi_T(yUyOEc#SCQ6K>AXxt*@~Ve?|9gl4&AHA!;1~a# zOf&{7O7nOhCR!}9Tg-OCn)j{=qjaxD-*JN624Kt?;Z!4@^{-i_cbkN29L@nGyI(}JEoQ1 zP>U1~n`_BZhmeOe6=ho|Unpq5#;FGh{CGIZ`Cawr&PPT$tZ$Bo+j}eh%l2$z_jISI zUxTEGUTq(@(?8I;kXPcfCrqTphHA{k9AGC_vm+A81oOHJwMc`Zi-pj)8Ke%zFFVXg<#KTOX&K% zffJUxR3Y4Fxg=&)1&k2EaI)0_qlnp!-xW8;1a6cVo+Kp>t1E1M>4tOEoWY_0+y2{d zV$cRMb9To$j6%_^wgTeS7ePOl|55R-oPErX16@_|U54~{ahfsC*_*Wt`nTD2XtN&b zALX!Px1^iDt5x`CszYm%m!AY{{6$}=;os0QVf?|o!XXQURxULfpT_fw#O&VAk$>OW zrAO^>cCnime*nXNE0Xq&b0G$q&!-yU(zL5pHdtXf^3)F51&+aDj&#KV-W8DoPE_j^ zaezy0ppAt(-!CAtEC9~H>C7HBx@htIideL zx9R+MoyrO4KFYImqy@0EDs*i_R%!TTGga4U)Ni#{nTA-lk!_x1e4Pv@JiCj2B z)$!gT`8;#~2kM{QC@JIkEjQ*wlX*rdE-)!kHuz?#Me|5VEL1G^%@`8(yJYcFK6b~dreP5|!FlJS z1`T0GpgicZIcS6q2)PGRT4kfxXLdTYHen7cPgWefS0Y(oUDlmliX4`konTLzgvW>b zckRI1Jq~KjWMiUD3d^>nLVY;b&WN^5w@rDWXPyxM0(ajD27?)Qm+{wKfK~Gp#)pds z&{eLmaqF;=<1+i<3f`{Miyk%2AwlusxFReaN%YZRpCz;=Ps$m(kBmYd@LZsRMwW?Y zrB+kzjz=s*X*8^c$YNIAT3`LyujqlU(!=7XKqK|MTGDo{+F!4amibiaj_)12FfvS( z*{c*&)U7shcCI^7CUnHulmrpnjndHD^Q?6`Ku`C5dlBDq=dPt+!hv(Kq+M|qSi;*t zCk#i^(@cO^N^UxZH@6aX*p&rAUkQ%oS=XtDCfBj!?-{vKzW#Zl z7Wv*3rZ4AG6wqfQ)|@wcZW(_0X$-^<_Wy(LNggN+zNoB;wnPvrpfNUr`O5BD)3-b4 z`T9fF8()>XzXof;78SI~Eco3qUJ{fCW_h>RqQklA!ObDxTGR$L!o1(`@bohvQTLRF zns9b)*ObRr_Y`w#BeG6nhuTzeL)Hk3SuJg9q`}lNtmaxFd)cRGe5N9Fi#r`2WBq7N>tlT{M@Snqq)FkIi{rOw74;1JcGG*}aD=Px zSrb=V#23C+Max)Og85L|>e{I5U=|>-WTRF3kDZ1`xX0&4b5Jj(Di4woRQs;o>>B!D zRxTI8G|ClINE2h8e(cBGguw#ja_O2+s}_PDN~T3JZXlGVx}OMJP>{q7l<=|!#NM6X zFgNX1U@k>j`rQo~c`5}9^JP=D-i_xLP~D)Z)X|HHBtI&(loBId$0Bb}WfoPAgdXrK zPLoKsuY|s}=`q+j*?C;#yLEIL?FO+%^NdiUZQ2eNuR=5H;x)vq-bZ|Z%h+zn09`yB zUQCbl%t0|6CyOY6*y6j!m7EUS;jD#$CX8-0Y@Y6|j_o0fL&1(s)Fzo0a1`J@Qko?5 zX|OjgKsfNC)RZHnr)A+laBT2KZcuxeh$kMkD|bFgyVBzx`QnQDPjBhlLS32aa7>-I z!Y&~8WrN0$UB$&blGaPodikRs=p9%o_3N~LyDG8+?wj-K_7mb4x zLA-n3if?hS@>Wn1Z(zM-FkG;AAy>-6YrwMnkp-QGV41n9Rln~|R^R-qFBP;D*}_-c zn_zw`hE$%kJiqiRdoJNI!wM5SWVDok5e6tr@Rse`bs z5qgsn1m4rj5x@cwAC-8lJ-bQuHjshsi@e%Nyj0EhYhtqd`WP`1M|^szFnR2wlTQ+$ zzzV;%KL(*;Bxuru^0J(lM z;A_1ndWK-N2=A4=BI2m>;PCM-tZs--)2g(?@xA)!XYNgUV(lC0%j7bGn^3bbc2XKN zLJkbz6-^r6W;Zkd>ZTi84~_(i%4(F(7R|e`xyfkeXuP*xiotwq|85k z$-|}lRWirRD^w=p7HIt0sM%y`ZkT+$cJAn4OTK@NTq_#i)0#5}s(-4ZzBOH``Kt(c z6Sv%HMH6aWUX1edA21xX)Bn9pQOCw-*5+rMPriq)I92k;P-l#KEt);d?Kmsu%@!^7 z2%vR6%S7 zq!uiNUeH3+NpX?;1inz0zlczbv8(T?zg9mATJu8x5~oPF9#00I$8sY{qdguoKj2rk z?0k_E3UEm@ch?LM?zY0tT|`Dc zCq9XMa@B#m_ST6E<``=%50ru5vU}~u&{x>z5BuXoHLki@g&o&nb8fII!u3yA9CC`-N^S zPX$_uM5{X@wcN|Q4?gxfXYv*N;)FG{=Y|iStgH*kQ4iIZs=A|72rGN_T~}9Qx7z5Z zki`T%|K0ihYte=C=f~{!g(t&;*!{B+k?#P~dKb6W|Es;o6PN4?f7xJXt|_@pu#3ER z4yOMI3ocTolx;rFelSGQK`f5q`DwLvwus8|h|B_hyH4xUd=t!QQP&P(@Z5T|+6Oy% z$E2XVQ^Sze*lvT&Nr=Jr3?^+|J#{(Eoorv71{-t{7EWA^N(8~UT$>PXtGgX|u*vhM zEZJeE`B`_&O?ckmT!_X>?QeYCsBh7}trMKu?@}6kdk&g4_A$~lJ9$UouGD#`#!%J( z61p*^5HTnO#wuJ06jMIOgJhnkR6{ zaJi5ZMa#jk8FW=?tb1nOab2z~;%0dA+k zGR|I2$wjt7pQgY=gOfTj46H2mU|!lHIJk-H^ha16n*zQXSxtO+@vV_y9z2jy}7rM{9iP%Y(LH~zlM30)Ex}|LkU%8^$Xy>%F!!qKPL)do7jysgSLdE3nj&ln zLz-c8H4_`qkl-tl^}LF?fMF8CYu%}QPUP-+72x3~R)&CA8&OcWfBkQt>f?yR7GBL% z%#9%(@XT5}0i+$d+6^9=X`crKW>j*EmU#!Sy7*Ytb|0#f&15v-nu`SOMuD~b^KsEs zc^Dzj)bnTkZ3E0%RxVV?Dg`O^{OKu#*Xd=VDFvHquxIRYtjQjBw%I(kt9>V$J1qq~ z(yC=hTfal78+0!u0ihFE{o$Sk%Eg>gGv!_xPqtua^&1Tx3igBZ8J`4Bhhm)AUZSfpWk+vpvDhIPc6bu0Hi}4_spR1 zpQCPTQVm@!h8`x^v0_06BFco*B6+o5hdN^4ioPUq1^2lmeu-J>L_fI+5nF!#wjDqjDDRYujD8g)FTf zt&0;!aeW=shkTA|aTv|Vi>lLq7DnEw9l#c+cQ)CB(LOhFO{Hhr@K(zK`*p_x2Wl{% z=$%TT?XdC0sC54{GYyq7c_!iVEW0tiDX5>q{aT@QOEaH-%4Yjr=uN@ar}fjhjGWRv zdV*!+meL9M(}I|(`=5g8@Ze0S-fmi5Gj;5f(Q(Wr>hkWbcgymrr1kCJE96xrXW)*? zzmRMG=^CxWxVis;M>n9>)Uz|~(DeQvc4`hD-63H+v3r|St~{7NRZ_fp_=)9TwukqX z+AxxbQ~Uf#r8eq{Wkk<=J8K4`|#0A5Nn-sq{66-3rH2?dF%yyz4X;H z!irXMLJc(zb(m^b#<>`x%hqGarlzRb{4*LYN`KO2=fxA6=cv)DEf82@-9A?TAwE)M zUt+Z1d>pD>R=B=Q+;UN~Hf5FterqZaiAYUS>?Y{D4IooMh(eO|2G65Ml;`y0kd7W4 zx)~hH1g0o}6r^j1Td3lPHf!TDd;)s=^w>;aPOzFYM3h=~z+U zsP9=ZS4;A{+F($JN?lcFpk_8%FntH0ME9GIWax&@wEryn$y%&6-lJXC>XiL|G@W}q z)BXSdmE!7BRw*inDMejkNzQCsDs?%mE}xR)Dnf=~&UQ$W3L%xlFeR3pF6Z-zjA71) z9Otw#=N+)k%x~X6et*9I+U&T45z>@r??OmxzVe^Wi;10mzu9y8Pn7eh0W9 zef+8HV4MsQeQV-8RY^5vWTcRdG`MSrjIQRrWM4R*5cI^Uo^=V?GvYef+1q_gzK!(= zoLOdTy!xyFH9{(XUr*RUh^%YL@jG|Rl-;w7aJgMrL1)lOC+F1(hN;2iSi+_VgJjpf)4`Y6|NsQnSYk6W%4uA>j5pH8I3ja@~ ztI?|5Mw$}i@Bit^946rlgKbgQ{@gtMf>;p!uIGxJQjDmZN>&N~__r8?vi;f^wIFUH zed1-%3?6Z@zd395cIZx0@!Ns~x<$mr`}b`w=b>&%sZ|ZHe{AGn446f6liQzHHMMkh zXsxK=nNdAZU=_1$R>C3#wcKKz2;|Q6Pl=)vIE1knmVJXMgjM z-7}{*7iP+ES&iGLU~CtcIY&qKyO@pD(_PNq8$9;Mnx|yMR*`Sp@wF>^}mQ;*bsjOWIk;ig!fIg_PNgPhw z9sK7N*u#BxqcQr_yg4JQ(Kd|Yv1>e|vOm?^SEpA?NbMr0te)HOquETg4V zlnZKRCgr;C?_Bl(@GKBYBRqWFtETsO!%IjafjUx!{CoGcgA6dEhS$iSeqmoI>ccHnV8;UBSQMe_z$yIrW1P|!=^^vK zNB}@#LgO;pkTDMzvAZZcxrn`|4?vs}WB>|< z=jixq*Ll%k);|&qg+z?Sffs*e95m*@nGd7_JSUBWIGVQ<{QomZT~GOuqdBKzy&N^1X6 z&Ft-$ZC$oxW);-OaBW;gCLB(P%tS3-gd519)w;8$gEV**z|jxsj|QyZA-BQg$px)OP|q!$WN zk8^Vl*aRryF@O^XBfDhB=?h<`qE`E84E&_=i_Bz7ibwA~Yl zTb$~0^s)jx$1=EM2{FEMj!)ZFRP{@LKuYc3C=nwOqarl{716+N^!bHvkdHMcBWh=p ziL$_m)urBs=6N%5hoCpTZFV*EKLM!3$zQh$L?1g4f;U(**&JK0g%12FC+9T30(NJSAyXR1_#vetILv}3r!kM zeYY3jm1(%p@eK5xQpeB;%5$yUNb=20^*Q@d_55tdabl)pfdfIjutl${IY_2c)(H>g<psVOorm98^fr<7p`YJxC6^b( z=pY}iPi34+_mBM-c0VRZ%{i3*^Q*x-msMV0=zyIA(njvF+$zn`gmR4d0H{EG+j~2J z@fwfvD%FC-9dK&cRIFO~SA*HOr6%H=dlOjJq^D{d|B%i#C-jmAB*;o(u8+GBHFI5~ zpNL1U0@XnQVST-7!7oC8)72uzpb4K*^VUmKk}jc!^En>lQ3SbD37BoWX>LV|^i%E{ z0$bmRCX~nulc}E4K?QiS-NEDiHw5`Y42@7qOmNi;KcjBVFC<4h{T8G2rD5 z#Z(6@{T|i0jnG&iv|ds{TTuyb8|!eKgfW;86xhE|>I7GVOw&){8~cdU=8EZ!PEsF+ z_4Ws4C$H3~Q?Kww-%z{m>eu>6PlB8S0I=9<^j{gqKlNZM=g{58S!E(PHs5CGQ%WVz z3mREZu@IR0yz8n7?|B#ztz|f^41rg}sz?hjIjV)$ZqYj^;yJtKIj{2nfw%-B{je|nn$cLM4@yRFcaar}TOC6FXj+CYIx{)% z?BW7cBz3Rx{-?%yv?=nSoR2DL_+R@bcjQQ zKShb)nU46a*kmQ%oVvV2kX9IT-n;;Dl$$PafbvR3f~^4q=G^DOxmMgdH@WGTE|5|X z^9#7oZFkIiH*0|EA(#NWKh=g*+H9k|ZTfo{6Uk_6{nD-+R3#z&{DY!aak$Dfl9@6} zh9jQuMckL8q^BZ>_(p{NE1tXxekz_ma^L#qopL3_+DCS^O7mH7iQS!#E2;XV5RdW7q%$JB2H59tUGeV zD=9_D;H+3(*XvMI8%4LcMLqCl+k^NS&_+My^qr7E{jl`o(3**WN|u^k1P+BFj6cBg zeOq%JUj!&)v>c(E0l6EGsb4?F)VgO%^PFgURR8pw{m`euf0DhI&6bz4;?>6V7 zd|NiaF-nu%n`5Js8?e>&qQMCT(w%W4J`x@vfLqoQ)Vyq49X2XS-9CWI{CQlr+L zhnJM8wT>lE?Bq@7&yQn6B}68U&&ZuWbw+uV$r~GmzaajAY7B%t96axJ>nOUQ4YJd_ zzBX*byy&PvVqbC>1enEWxkQ|H;q|xV7I;pt2q*{)`?IZ(ch2KlPrSwfpU#%dbBm8q zB2@c&8*M`(kQ>_IZq8kLoxdr0R}kvc1G_@Yx{y!brqZySDs7#d1;ap2^+if z9>ZNch+*Z1^*}!n~PHGsJ&sri zAKm|mf#SBhbRc#4(XorVKxX8E?>v?q2zPE6&Izkjd($W$$EBZBpVqy3Bq%xw=dViH5 zZUY%tXoaTl{n>%Ifv%NYwZb#cbRDCL;b-HV(GfQR#^J&eo-$rY*W2$?ZDOJyQ)p;6 z(5GsrHCl&*f0J2qS5GpD#Z};SXg2Y_6#3SLh{$GUfk}`-Sb-M%Z)n|2Tis6cKhx6- zaXyG2N`qR9`ujhs_4YRb`O8k-4;55B2c9Re~!eM6jt`zZprar4vlV5P&n|PxEC5K7p$km0&x(S_#GG48 z((rVE&m-=;7~-lYff#?yFSMVRqzD$T2wY?QPSu==LbQFUB&5pJGS_@6=o|S@?U5c= z+bO(na^^KUMZ9T+63(c;Y`Lf_efK5NvUupbQ+MsndPLSHPUf-8Qbpd_4T-TY*l64B zJeb>uV=x7t@#3eR=P(Fw6tD2#SBX(SO%XiCBsgj{$(1*lT zi)N+6P-rMR6n^qivI|j^kRXEn#9VR5_O|uQ1h-uQo41agit#rfL-@>7+O+{6vq*3u zQzBW`*N0~&H`DIy_E0g!SX0&iC2KA6Z%s-L@2yaQxu~+w!MO%4WHswm&d$L{9e`XX z3gMx#7x|M*XmA;U%D=b!LaBDV%R)%}KNx7M%KSLVgl;`y6g}t{>JwnlcAwJ+t@p};w)draXCknX#!#Ue6Z4Aw%rTm zu=l5qHmfxK*?!<8FvGw~0v5oR>D>B7!GJ~3EW?r3N|@ITM}_zx2CD}cr=uKv4|HY* zwc6=i!7eNSN*)dWj0=lb#}XP%vX&TA^LIym)`p_1x5gC4QoS9#4)oA3Nz12%ZaJH3 z=0O7&9fNgAt&*r(Wi_bx{K{7;yd^bAE?W=M9gER>%Hjpup+ib}zm0kLX{Cz`%O(5# zlIqD>k=H9-8}khIe!^&m0;|mwcq-!)a&Qs1=0_(xYw+A(9&>mdCt^qO zi>f(Bl{=%-H>V`LY*gV^FX>9{QYGhGj&Rwos?gtbXW_1m;uzBK+k7d096qDC&KXNC z-!4F%uGYEWTi0RKtTqTXPZ4C-cgmctTk1m~TE$@s4sxjD05CH9QtxLi$Wq|jBM>a} zK&IPNuGyj*}6usES*PV6Y^V}vM2nkKb( zgyuP?Q~=tDc8gIq~ZYY22rH$Hj;H*y1L+}XE zK=2^3I+YOc_tE-4RE^u2L;v0ZG?(;hnfAQS_Q@zdrUfp$MLcZ3>1o@3JOU9hw> zI$JT8UQ^GB)pjuuo6|Y3+S;-Rmr~mMvfYstb(ZR*%WZ|BroaB~FrFa?MU!OtL-Okx zK!PWn_z(#Bi0NFvB0#TMo1ju4VS(j+dQ`6=oybgjQ(Y#<7Hzo0Wk*n`S4;?-4A|CL zJ4v^#sl?$o8jo)S1Asa>V}&%bJv5u%S4bM;IiV z*W$u5!~SZg$-x98`K^CVncb*r&O$Yeb5(61wPU^Qq4J|F^KMjM$eFJeFr^tD9D0Bs z+EQKJGGR2NU>_LHI<1s_V7ojA2op7$?22b-8O_dHX8DGX;l(6XvkJBF4(eP(KpP@X z?cBs_Eof2F#dtYyw-PL`8BzW@1I|xHj16W*UBUGr1)zA*|5EGqes^%K$2{OYXxgn! zVcoS2uMu1*o)SJIm!AG_!LSU`Ef4BP$q6;S({6Elaw?_hE2~=U&U}-n^cANeAPtEd1cYE^;hOSas)Ra9v(CwE4n6YQAF-M z+=~}2&JbPP=JFjFMgP|$Jna*nugP}`ohf>D-Oc31rPj}wwPiSJvj9fUe}GKR3_LwJ zRJx2^_t#p8ahkCF#rhxMq<2Z}F|`;)v01Ls&g--Z6Gb-{?T&=nAHty;aEi=)cG?#PrcyHlduz=+RMJN>Uc0uZb}yWl11fPseHTHvq-HIyfX4yNpR zpUr{SH+952{ZK@)o)>SKyqNvusFSzh@==&?hvk#;%0kkjgYJ4O)@Bsh?|zi_t!nEc zb!wERnOZihRG+C^nC$hpN&Qe1-|sN}l|m{jzRmVnzvMg1wf|iAqFp^`Dy_jQoo4`( z?)x4h!hGV*A6HucXz@=x{wQq5U5wk&KLPpQ-)fQo4&i7<>7iMM-v^DNSYK42-4G)` zSL%$U_L!|E-+Y2o)`pOCLI4EMn^sGc5@t8$=FQQm0c}^I?<~Nye^=Jte|-X-Ev%Z2 zjqDMD-kB+rAPc0lyYULDKXp5nRy=QVP zVM+^KPkv_XC^Nrcphk0(B1Nq%5KFf{CfyOTsRh@TPZ{X6xHn1gs+O+|=geF!(G(z? zvU^b#Kvw`+T=W<9Y4%dzWN`%Y95@$%gF(K!Af` zkj@U%P2)xoyuG0C#7hRh*?)J2Pg zC=I{Y%`u;a3a;7d01^(|bngn_Mwi@R2OX9TjU+m3#WI_i3QZU$9MX zCzj38n)ARpV;)iIC0lOZ)~p!7iUN)BS*eWcw36{Iw-SyGNIK#k2Vdp>_JoA9V_|~1 zJ-dUK0u3z~%cig06*Wic8w| z&)g&Cfko!?cPEzECQZOrU&B0SV#g=Telj*%58&g*{Yu&ABpnuYSa~;mteQ+WsvN7C z{G?)l+iMhDawN+4I^7fy?#G=jhIGYEIq({@@S22k2EroPZqDF>O%TFaM2 zE4^G*Wc*X5i(>Uo*)(yw@Fs`!FLw^c@J-gbQ@cRny4*w%y)?{5lIO=CCJG6MC=V(% z>;k^Xt~DWh+oGDBD5$P6g$?GH!Hus?;$KaOVK_q$<;1xOw;8d9uJOTv`~*E?$W(06 zr3(IL+g$|HlU#PfS38*7QF7L<)-f#k?T#*yi!acqGT9XkWL8Q6tizp=RR&A)cL9g{ zlhvg18i-*}1{*@Zw-O{6FL8@n;mr9oBoq{}i8>NTXxs!9`Pg(mcjYAkQYzIUPqS85 zt~`A6NRgtK#VS9d2>zP$AjcE8 zw`F$W)|rz-_JjnKO0q>txnF>|!y)fMxJ>??k(N79A6`uXgxpxMt?Azh z=M6jbE!$e}e1y!4_NuZRY)VFPkXAc`urlzNpN0@Thzf{tSvxJMkeCu$xT_p)Oqv~W z`aqDIHcrb4D#=#kc{@3DVKK4MB^Y-*Ecgzp@XYT#N6oFVpe?_L#hckB*Y?J9N(7*w zt7>LJ9>y!ynhKL5^U<4&7OIbs_R_ty1REV2e3Xr(xqg5bj5=#6UXeT4oi#( zty!3}9H|i?tYQF2MWD0tRA4?}QAVfvbsc${gyPO17 zy|XlCzY?ig5#ZYsM|oQ%X)gL@8+^k z7c4r0a!%;Z{ajK&oQz3`%YAfU@zuBLpZ#y;PIZVhUx|m^;1T0rbB_6bnn(oz$?}V? zqSL}O@3o;5HX@TUYVQ+x%W8rzjeoidFAE&{tk-sMKoRXT2xv-ywb>~}e2BNh3A$G} zz+NBlOc?oILv`ER63TCdanE6QDX8>9xzilpusjMe7r*lu3a)+b0o}ghKYUL&mZ}dK zeKhCH;4XZYP7!!9DS{+eyC0KgE}yaWzxB;X6P^m~I+&;aUa$@Lo0f+No&_4c&QJDr zH5d+7aV#HHun}-#SK-k*+X0s_$VGQV&rqpxbI|cVJd0NE^r=+`yBRp(V>2l~<*F#L z8Qxd1Xs14f3=jPeF>B_5H_mhx93+ipVd&Ifj3~GHG{ctX&{;L;o^4&uUvDavnrY8d1D0R+$I7tUUAN{-y3o}6#0_2PyZzY zKaCHnme(J8Fry9{zkh%C4x)x^(fh50g*5h&1v+mgBgVAIIp42Mp0CQD^F(bmgA&U0=|_IP`r8E6f-i&2XQuSAz83 ziMRIj=|y$OcsjQydvnM+8}84datgS!qy9CVTq>ymvNeC$mU;qdkEf?^3@3=d7n;&V zrLKvz81HA*d*+V5gegB3ZuMIeN!UGl4v*?KuHi+w%>6ILk8vd?S=JdxK+H!f5*&@- zrkOVuw><)S9G9hjV+%CUfda3duw(z>d^mmufa-X?pRE?CN*Nd=A|6;z39h{^In~6W zbsV0XVt<)j?^pGu!83f>=czq|CcpCE9zm8Q0=EWk0rzJazu?%H)gCiZlAff9L|9;L z*;x>OTls{m&(U&Mryf_(Ugc z=<*j}d+|N(SkSNL@sh}2E}&T8UOt-7Q6X}kl{3dlu-IwG%e-Li=Qe%mQH=^; zV6&-_X}$b_JaB}mW=XOOX4aG~*L#0=50c#L9=u+{n7_LqFEt1Wo!5O@dL{;B*QSqr zjaXL+YzI=WWKiTv;zOdni@26&V}0oVNgk=Jmg_a&(*#skUrxm3ZcRegh8XeJ>KKOD%@O#j|&E%>$Era9C zo<`{*D9l#l-zop-c?IR-o4mSOpWg^tT%F5J*UuPUtwgOnaRm{dtL-gvx4GmV|h&f7a9PVt>eoU%|B(76TIQ5OkHHvOPi@ zJv6z!dX$uNNvee++`){c|5WynhtCE)d}(lGvB0+H)?nS4%-4)-LhS04s8NON|KiE2 zRL2Od@&NGVD;c+w@(}iI7Ww^;QH?{#T~YgsAwAGPEt%tvbP^L?znRE)_0J; zfE2UPVbg3O>t-%B({JYQ8O4ofw&B2`0@AG4d>itQ%{fcS2-OSr+^0~B8}r`7HrV;m z5BC=W?h=e0HX{TK=;9Fj{;oEZrH!1QEsOzI$W}X%w>kk(V6)Vck>18)r_XD|W+{I# zG(DXc3-)JLX>NNiG-!#w$i@7n1?SmL$eyTWVrQx3?YtvqB|_aZVN3bGqDw2J_AB;a z{Wd1z$hebS@`lQ`2k}PMR{SS?0(n< z8mgH<2K-BcdpCN1&=2Xa6ki(^-JqW#rglLT9#`x5T-|4B=mo3_en*K-Q2+*j6H_Xi za#7$+`n%rH?)%{X#8vmzx(U#-!0y^vqKA(apE}h zwl^Tr77Ep+koN#0#k0R0YbK`xhQS8;C*LYV?x0k-{s98oS5sHPDKu z%Aj@)euM56>j1)U^kzfm(NrjM^Z*gvrvMZGabD-Hnq#w)SKRcYJ%d-{&_&)sVx#P| z%i+}r3&V6{+uR7(aK0&k^~FRX3o#smD(U|$#f*5f8KEu+@TG--tBLcV*Fi^tz92Qq zE&l4hWG-gAt9TsAXQWU`OECl31>j}7O+2RKqU6uD)J8jh=Bv2It-qyG9CN&vr& zl7cHXAR)7p_AaGtbX zqZQd6CR5VXGi>O>ThzzN>}YK90Mf(LF4H4|ZiS9U3aGJvX%z3;usL!#GeX`yeEMIT zQ72kX70{<{BQ;*r}5F+m&0B%9cY8saLw(| zDYYrVl)S=4itWyox(}m-a>HepgZnQKdg9AR{%2I_l`Fi|pKp@w`vVfbHTxrn&!aw5-LID>xH=9ya^Mt**2jW?S~_AYf~4n~ zUA}i9%f+C&z0Y2WS=*Nf8oLWETL;p=3ZPi#K&j*cJ81l87j)k<-MAixR*5`shQ5}m zy?&c=R^AEfe)N9UT!yMRo-N%JYWWYUk}FR>}`K#yb z{@5`52(94EiKNSHS$@I3K*C{V4}smCu!HD^7xzXmJ#ug=Q-k^q&clMs#I$P=D)WonU8yhQD zV50K?Vl+mx1XKx1)$FRvWy$qlPe-v+D=#P+I;xE2XKveJMcCa&e@{>W0_}j~Pj|!; zf}B@rl+dmEGj@UsmExW<49_y?RC|zc08*We*o*)nb-~z0wYn(rZ7fZ@R+aNF3)7Qdx5~* zE3mSDp49I&Vi;TUsk{6h@Ec=KK zNWjc-v)I@7U;}967PY0 zBXiuQ_LH6O!ZX&Rl%n`gK$t$7o$}sA)r!B|1yA7Vx@HQSBl`PC+eKm=NCfNKNd1$3 z`*qF!eV1olxQrmvmvDey3ff$2LuYv1#U*{;YiRG2Fl?qw$%(G|STD^lVSA#(G^k?H zXKMz$Q5G|{KQ2>Zu*gfYneTDy=e{Z4<|YHZ!bgGxj2|vW)fYiZVkL;RLu;e)3@cd8 zsBc%2li14tdblp&adS47Liut=6`1MWkzAgo<+v?j5GnQhSi);POho{AE7`f-Z!pBw zRIahV+kShN!DGeWw5mMyxcQIE;-o+8@$vxJNqIy!6!>Q+-E;o1@4JJzVBMCUc9(1; zZ~4E_ZikjgHwncNwW1 zh%U+0?V}C|ikM7*FW|?ZXB%+;pY|Bll93zy^w>c1k#qwx!2rmWCg;uTj0;?RQsj3( zm^`2MvzuNdG15`A&gn@Cm_KcNbN3IQt;-{gP914@`4H_O2wb{yJ0Qqz?Rgj0fS^|g zHh8S0@W+xLHr}H*#(IXyw01XDG?v^hUH-uAZM6h30rFo$x<&C z$eJe_I|QwFC)-p`^CXK?lS@o)$1bL?RS>9;x>+;NuKi~b3&g}n&<}$YBa|iA4hPm` z;P!@~;cG?D3&?!#z=1-hRK*u&}9k&A%7LehX_63Z(cl( z)beG!Io;E}_fH)2K!e!E)xwkUuVLBamQN(?@h-&E4KYV5goRgeB;_o(cxJ4HS(N9* z3?oXo(9-zr5^ir{p@1-<^t6xrEsWlkH9guUs-!=-na$MIXZgng zi!#QH7H%|-X|}UssHlSte#+B6e)9?56y8xiYbgKMid<~UA6{0g?OcK}(YzUOFKeh0 zV-R1Zg%+k(yxqTpHaNH~cJKXSLvy|fZ~65G=1Aw0aQfa!O4eLopNZIeUpCEQ(bJaC z4NWg zFjEzCNwim9?(qiB?_IuOP&7hwct2vC9t*?@{Bbz}sd2`$2b}I60ez2rf0fPQV_jLIowvBbabCnves)H)0=&HyFUg^o z8y-TY#`HARyw60T#w76V{Vl_JE3$x-W2>sElrdd3Kkd(C_6a`y3u+8+#L?e$WcLp_ z1ziCJNO^#oqpCAVl28a(5Lke)^Yb*hiLKbs7GM53MvnR(@or`Oo=I`|@M2*-dDP_| z<;N49tc0SNE7WrX65KNJ7;N!@2F>vDG(klYaf}4 zN?@eRUmXY1Ggbx*HV%>kXYzsIEM&lkeO?wYkUa%{25a3Jj=K5s%#kSR65mctg*z*| zT@IYHaHfhE%my7x^#c8wuNw?k71hTa1NoIUC&Gc;p{=(iJG+RSYH`1%(@Z}VY&6N36H@x;L zlvGSVIjx^Y22NXl1wLt(1CIrc7T2A#XtGcz+E)*{ht`ngpavZqK@FYO;kBjywc|rA z$wk!Ls}gcHD+kA@5_a5<+jWfU;I%Z`O>h~Wd_~Si3P{2E=zZj5%)WI@8YSsWAFu3LyrOe$&6rQM zk@z8B`8e~3_-J8d1O=;0Fwu7ItXb@uEwhmvN7*!#U>D#;!><;0&6rS<%f z3PM?*+P-mM_;X+B9ad5kYIdIU0M@;Iq>Sk~+VCvIm2Z`j%&``pw#XA)YN{f~KzAnD z-O0?9N7$_b*xn5peDawCmqa1vX1}8sWw823wyY0<1AMZM%9_5{ojwIdE41XKB!*UU zc|UKP(P5b9E6Gm=Ll5iY!ebKWa46gL#4K{t_RfMvM?hsZfivzYbEFw|1 zz3Dq-_Vn&>QpOMe+G`sD1X8>+iB577@OrV+^cUUVwjSlha1v;v5W2L$eM&d15SJw9XuuWMUvx{Fe9>Oc{&?n31BdC$?U+? zt)A$0Osi^LdU5bhAnf`M&Nd+daY39;L^KWE+WH#Y+_7n!$2Hk4 zi%Sdf!QV5<|77{L-AX8U#h8Da&@(*pPY(4ppl{$whWB-K4U~_-&5R4p*@pK*V4NKNfs_g5{{cG?O@*^n(oC`vUM5S$i0c%*9x5}I&q zdg2Wfs9hbw%jCFKcpF|8&dD|X<|CC>;PX?4dcuy;?l)?%8CB<|P&o5VZ1?i+P!uBC zHL$8Xq1LPXS@~9EmNjZcsEu}Bb3r;02gne+?JnDS?)G>nIZ9-+BFuWoZL4`SD=dgyp zJHoYQpgm4hA)`RH*!ezUhHg`awSI_$)r}!+LVqb znL8?wTNR_YOP$hAyImY+YF2`&I+dl;>yT1e@_Dj%1}AQi)I_2UgwAy-?k&Fn2R%+; z;5K+4-lM7*3}JfK_;b)?4jOpYvuOLmsKN5@C!DPNy+JL-rF%EwnZFa_hfCo!YQaRU z%N$|C@LDj)*ZRNN4}cZ-&R`duez@-u?X2f|F#hW&V1J57ak~*LzJ9SN!;q zb+xY0*V8D|zPiA8)-uBvoS1mlcMAnx^?PAL%4a2C3( z>Qddz*|{TBeZB>pk`p$ZZka;K54!>}xzhp2+GG}m8Qrj8e%cpB_|iksbIJ1)fI0sI|`{SC9YSJ7v^2M($UOo#5;l`0u^n7YFYH^0D;960bdVXtd93p6ipvIg&K)z}7 zD<__o^e|$O25&lJj{#YQJ4_=20Fzn&)JR)C!F={S$neFC(M;z+*{N!$8V(pd8@=egCo{H zy?Yw=6(c=`dz@^^%)7C85k7fAU5~^eKe)E_I-+>0d_TiVi$+~oG&00TZuPw0LV(s4 z{;5JWAE>bvjLna=`uu~~ye|-GWz7x*iXA^A@FoClN#8yoXEff}o6cT$7|0{?nKwq} z2-5A6%1<`ZLApI&Nmr!nMkYcN|^SPqV?t zKTiRkRyIKHchWAR?*%PAs2!nmQUkNLe^cwdpMrU5NzS70A zt(~Nd_gCo4T4e9jQT}LSq*K$@EPNajb4Qw{J(gdcI^%?KD*$6hxFi0=V&RPuce|!7 zvc#Te;CNxblV^6Y{3J69D(LSKsg<3S%lSS>n{; za*t+u_=eKTOa`@}Bx0UpaEL(XiktkhIzlWaRk+or@yAdI*f;0S{RJ2)th`mSb*{1U zBW&D_Glu8AjJWa*0s1Ux9yAB+1&EJYzN4q?E7$sL^2DWovCpD++ypZG!>5<@ZO>G^$ zoG+v8DG-zF=C%a7-}A|zmoX??%$6vbZ#*@lagg&lj70i zBM9drLCk6)e>Bwr#8aV~wnQBYV1;E9Ixwt+Jy3iY&>u_$~%U(`+Ehr1#@ z7ygqCy{I`vq&DrX-dCBu=X>zrf$B&ddrw90(|Q>BqF;pqavs!Z>rArxbCULbzNVf_ zok*3nP<_6-A8KDGmO*%c(>IZMi0%JP{`{N6(?kDSbUaj(2I3CKkUU?MxOKdXgw3&h z%!51J6402sQ(y1~coe<@yq`u~G@z=*2*{CDly2j~tr+kZpwEQ~ryk*g2?4KJw-|K@ z(!E#oWaOM<8wj*KsCUAzIFzUGG1(xC!)Ml>9rvgR{Jj5M@2p{Yn$ST|6wms$mT1$C zviwxMurVepj-OCx^G-rK8QpgLmRZL6gYVHb@ZY^7)guq#G*g?Ig=amz4mhS7$k*6y z8NLRJFZx27YS*0`ltg=fEqKtN^tg_&V2Ml<4L8)Y-=%Nmj}*dm`uBb;4O{4(eZN|m z5~M)Q^?^Sgk8#CWzo2)wV5wG|c>l6c2UmW~%*Qix&o#)WGh*Wf-qxjU3Fg~_AsvKG zuBm~ZFXSum-vKwxYIJgubld5SN6$n?+9Av=g$w9ugQGXFyJFyKPGxw zkCZA~J~Zwn_d)Q#E>T~Jg%b0$ok_Di_hbp{-^&OVv*IyBT@LrM7szESMg9euVVxJ@ zUEBnM{!%Io4pnq>W@R=`TlJ+O3Xa|212?aTvsAFlq&)ZYxOw^y<8(WHABgX58)1&K zQ3*!}D}7?GOsAK<@geN0uHic~2(C>=qro`eI_!m10_1Es%YSZ0oV&2abc~^ABVY?> zG^Yb;wsr2Y66qzLBVSFTtizdUD`Mciwo_{leRRhjSU%N2r2OvQpG&K2dA^2?r@flg zS%pSW|Akq1#2BYaRjx-%XY4)>J8c^_QE*G274R3HtaE$gz?W8Y!60VWNaXYSKFfhZ z%93Ezf2V=OtgOgo>jLm({ZML(H3hx(W!hBdjAC(ke+hjV4m!*yGzz3k^2AZwbK9<% zYNbtuiR2Oe0ehUuws=!Yb6!e?_cGKFyiM3A7yI}ZaaUUwb841uxZ6q7vy^R+UcFlQ zZHb2D7$QpxEgd5qnW+~E9`qo0zpE#{QFTy0SbgQsU&+tf`W7 z*_9w~5q8(Nsx4>puR6bE#5Ox*4jhgZw%Bf|t5KlH7bS>{zOG!}516CLjFIHh-S#5j z#XM}=QN7W>jddaW6r(pyHM4A^ELR^^=W_X`e>|Oe)@gcGeCLnKlL=tMdywJPZx*(Y z(bvmi!xQ5#MyqAH`Nc(t6H6v$(g49PgZ8XUxWAL?U9$g!lE$7-?+~)`RgB6rnO1^0 z2yoC+Ekpae4u!})fZlc&&dh=^a^n>Qz!HUo6#5k^bKc>unCk83 z>332cuTFC<#nta~K8ETpNuGTF8KWP21nhXJN)~X9juf`g;4&v*^WI}zwVQZ>pRcs^ zc3aZXbGwoLhMUXmuH2>_toVmaAEEj`ZKKs^ysab1tFvFx6%RE!8pl7db(3{7=mhZ; zYOWTX7Q$)fFhh?NypA7h@i+rNjSFeInFy%;)b zdv6~F&IiP034S2nuOv<)T44M4??S{3{F7E`@ z!lx_BAq09K>B|~FLObDGq*b&1@zd=R^MiF^>(Qy0O6x4)2n?KWkIcKxEg$&@T%^m0 zXx=58ZYf{TaV0vlH^=OsjU30g_990Ap8YRrgGG zy3krbB)3#xO3g3H1W+|yV*guPNn@~!hqDvg>;itck7MtYJ$_S44TE@Zs=v2<&7^Wx zEdl&yad2rr=w@sdRyT}U-PFwX=a&|1)ot3Z9LOpTytP9A(8ItQS9``LITOdJ#n0=V z=tfh;zSCm~#Tt?k2AQahFHI!F9Us6+dJe9Jmu4{5tO;FK#EcWAQXT0T5p8T9|-L2mGe*6{g?CWlFtgf+D(pq&#a{9WyUk2fegz+ z|H?lR*uPYF9?CTt?S9{xNrZ4-29;Ra$V1_w?`fujo^tP%dNJx!Thlnyf@8AVR&`}p zVAccG-ONRSO&u?J3voRp7@RQ2R6WQO6+-6xDHc0hE zPrGHDd$FK%_R>EAPMgxFLgxn4kB1u1mg}|i1A~9lSFZkhedVTGgsd!>*k^}bF-AA@ z9*W(B1%0LbRxOFo*5}EAglS#FU`NeRty`kkepMjqXj-!1r3@+^<#h3^EOlIcIUdDb zjBc$lI%e5`fXDC~YFBq8xTSV>i@%j^*Q}Zx=Y`ilDV&~Ppq9%~wf5)iX>4%}NvX+t zhmin5rKxd8uG#B)_zp-)cKe5~oqc+7Fj~??X+Girke4HoKwCeBWx+t=M*T9m88f?e0&tnWHcZU8SP3Qj4^#A|= zN+l`ll}d7|SD{`)5_8&CsnpA1dR542MIpm5XS0z?juVx$DV5}WK5tHQ$l0(Q=eS`` zn_-)6zI%Tzm+wEYAD*W@9{0!fcD?<(14icJEft(msDQ?*j4)sD@SD6qe|h9J3*4G? zqJr6~OP+$(9lTy*2ABPVmz&Zh#gr@+$pJVu?0l6dsI~XiQmR{y7!O|==}lg?fvhn` z;&;0<*FX~&f;WhtV;%0%4N3%~0&koFp(8~-6asNl>u;tLw1f*+o#9K1;{Tp7^%J`; zc0gTP{rFXoTYsW~&1SnV^Ly2G?GS^Rw3|#tnZj^AF#dIb@Id!oSx0S2HVq(b=be$Q zAH+g%v%tfhrnM2WjwMcc+5(nvr2L?$%0*jJ@9O(p%o{uZl*F+}+aRBRhN+QvfGWkyWy1=|HmxMu}{zLZ56OYQ(6o zQp=UXF#jY6Z1-6Z5zM#rlyDGJJt5}y79j&glT=Ae>vQ$+x>hvDHl*kC+10~m3-DvB7o&C-^&wbpE zcLSTqF@8}-#L{)@vkEJ1WEi9p?iqKYGolYH`z-Cap0y3r!35@B8&|Q^P3weHv>6%aQlwS!ceyO_W3B zttb_vTA^5nRhz%v@=%phrxhG7Q>eoZ7VV8nM@7q-kEVoSRES5K^^MhUKzrmd%$0eO z$tqNQ7Nz!({P`iQf$zpGbp-0@NU|_BDT{Gue=71)Mf)-OVjoeg1??DKyZYwmHQtTP zps}QB#=fA{=W@&4A?2|%W(vIdE|wP;9Vx>yvuj(n-yL|~TY(BF>5sMc7$Vw(KGjhR z3_d&ic+VGnnF4RWb{X;B?fsSQRe27+^rOWd3andBUT5TeUojaq+6#G+Wi1)2i>q*1 zvy+(HSpFJuw0`Z?qKj+C(=U{}X+h+%`Wwil!h1_;bYqHE2yTbU-kkd`cnJw~uMuyr z+{M)gd}vgtNkg~QEvpo6Adit#42}KilB)K#u`LYIJL|VJ0t4`2NmNJ4T2j0J9tXF_ zNo+i{ntLB^c(ug3>4>cD#blqd;RBaTtYz@C&MLg__SRwlMp{k}2$HP`KlWqNW$cqe z;h(N>1bky)zmy4VW+3eKX@D)|??%$hzY*w0>YE4QMnGrlKx+;ySpe@IWzkf(yK3t5 zHM(#dtIY#Dls!IW+~B;&A!$Y8hp$4WOO;-`vW-B%q8l^cVzH zL>@9Bzy8t2x7RCVd+BM&{i=~5M$es+Q%02=-vCe+cf=kp0jo_UIMd&K(WasOkMC-# zg#iDqbn+;CPO~9B&>JoGTcEQs&I?zsr^RGSvG zy(_5#|H_H9$8E>=e<(VhL&L2E_@E@;3qRQ1t`$Ck|<+Ocuq$aJw z0nL@q&G=gkll!0R3DqL(f6-e~>f`Qh0ZYDLzKh#K&ZR`DL4J$cv&2>o7%-g~%{{Zp zyml_<3=|)*m`$SYOM)?8?FgL(@?5IeN96*Uy1e7f6DiTmTZen!(bMv&dZ4uC94s?+ zZG-Ge%JbFqbfHRF!Iw{^X~wbh?iOIKk%M}*!6C)e1VCptRC?|_r>)|V!UU^`onY#3 z(5Afr6EKs;CqCftmk1HqB7_#`TbK`-5)Twy7Gk1nu%ar`Ht>$|8ASxfGgCvv=%YDl|#eG8Z-F@&ikq}P&!U6B&b5} zF*V%0ww&mF-(OpPb-cH6iuXi+ISaiT8Dy?9KhZTLmU)xUaoVvYaEGOy1|PKRy~PS$ zR<3bWwF&KBMwBm(d8!b$I+E5%1*fmKKU>ysvn-Tg=2D$WxtjDX6i7QyuL>S(=fqZ~h)>7Rf z6p9)XMU6{a(T1#IwRW~|B-MHrnu|A16)G_-FK%!>_qGvxm8-|?g@b~#u~SL6!Ikn( zaz@!mVv(cZ7d+x6?QCfxEgm)+5LY$PeKt`w2X!+^>U>VdUjr60q;{X4`kk*w)MOan zRR~d7en7&~OBZ#CMPoHkC*kdO9fnWF@N=`sEg(_ar6Y|2vqv42bSCWK38|NZ{*5DQ z;0kwM7(WBY7vP`{ufm;`uia7#d^^ukVdQQmPS_xv%~GX+Ub-6VOgU_2;Gnv)0}CQKf1(k}UCEXSze2ds z%j=N$5^$sMy574VFXXS7rz?NLdJjV zO$h9A_lhzGuZVi81x7^Y)Hhs`uP}5DDo~}L`KI5n(k&>LM9;EvOm1j9H(0gLB8$rV z?&cN7IzvE0tf?=wR&z%US@dH#29dg})I4En1h6UQtON{p|dS%+(oxk<2a)hMU zYhOP+Ka__BY#0%Itxbr?Wsv8VFSkTGh?}hA7qcWv!rC`}lr&(Tnm#nzjR`bDVxN-j zE6J$uWoKaVa<%Ek{S*2IE{xoXANn<955g{XkB!CYD8EvSe00e2SzdPEq5qg21MADH zHE5=i)Vj%_D_o8yg92}oUS=OJH3W{gakPBn4d6dmi#L`CycsvJK0>wbvlY-)|ipXzHb32vy>Xtj9~C;rr1_l-orm&pXt_xoKI2%Lr?KS5HC{NvK^83x`K=;lr3 ziSL5?&%Bto0H%t??$_EKZ<82ejio+mCnYCfpi`!+EY+d?i=mA?=PO{y9f?TCaU~J5 z(@$S<68Z9HNJVyY+2Mr_W_#IuZOyF1w&<~D-Fv2a`qLMh!8s#&CCeQ=OQ@$>P{+0^ zqW;kztbFJ@FjC>@bw}JKL{7igI{^1nyHP?Tm*UROvg{b)hCwxkYq)p#-gt z=KNRKIpbJmwIUio<($O+VJc6hBrb)6?XT?skgh8xBygJqKhbh8c`cUHrT`OMkTY_6 z{loFl(|x3zly523^8<>*FFqbbjh5+fl@5SyNNx5I4$2|t_A~L!LN^+emnRpHR^Ggi z;VgqG^`r;Po(dH`$FP>YrA;E~aC6l2g$ktcgBQY*kgKDT6-?U zv?;R7=9jaa97?s$u-_icVbB6clA1z4Lxb*EMQpyi7Py5t#L0U}ORbD*N*}K)Z^{>` zl*y(U;uRC|ZIs(KSLOD_5MA90d_>efdk=BNAmfIC@s7F|>80Cd14cU+GDvIo5Wbrh z%u_;Y)`pOB0%+4dJ6^Wg*wAzAF_1c3B}T1CwEzfiCkFjgyZbO>dN`-$ZbZWnpV;tj z!an0M^5qMx@|f`$%Pho`H^_7!jIz`J!k+FhP3g3R$fLODZB-T0M@tnU!q#TitsC`| zP>|q(M=i_ujDhJo?d!=}+Y|~L*8s~ZCS%2H=bIWxU{+j>zgI6Ap z^R^8oW^FRut~MT6Uh-J}bgEvvIXA*2G90$+$@BUEFv<7w%A)uE-GTF&9q`|J+=mFF zwsd^#!fT^~T#5G-#!U?}2680$$WV7iet25D?0(D()exJw4y7T{4V{0r^93e)8?`w0sTq6uN*Jh&xCu#uNnR`7o7_3X^-VR?A+ZRFv06qH8_H~uTY;_awa%(!1|6c7Hn5_5dBjhD2=Z! zd|oEoPq|Q4J+k#$O0AC3Q%wSTAsQ%pv>bMj#g(k7? z`g3>mpRy#9+qXPMjXORCVOjA4(GQ`v)MzVs7vc7Adf>+mVCj0C6tN-+1n6ZdpsaKmT$&2EU}rU&4FrZU?ioxytLSl!f~ zBpNAgB6j)b1DlX!gF~;|?>u1D2e!03CUBJ;lti!^c;0;_{6@fNRTErM_o?5wmGa&% zT)NnSyo}=<*29=sz=C0zZuLG%JB?PHfMq*2=!|4IO{(Xru+KQxI2gi?arReH53bpS zN=M*jMG)}T7n1w9L@U$As<4i`1cir%N-({z(y*gTv*b6ramqm&H+qc<{_tnheP1BM z=urewu^`)FX1Vk_yh?LKS{Vg6$%)0^W)8ovc#bFrbbJ^~hM$+o%e09Z&S+>dV0_V0 zE4dqCwW`koR{4Am_x}0`phZf(BJh@8R|HSmSWHl>d?)RvY{sP@g{;1r1*&fiFgm4W zQw_=#F$@07HAc8eUCiw1T?5+<$3DVrrp8nYEh2VPvp+o-gmi--bN6dWtiQF|EI2gm z=BR_I$>q1}Nb`elYdneI^-D<=Th65fS7>Bfqlns^Y0L9-2`YN|(gnL=B6`-&%A8wB zVqaWHV@j{JW8Yg~mPow#G*t(M1{QwT9J4p!X|MZyrMDXuqPecvr5M_v-|m3YeyH-l zjaMXETRyygLt5`KH5-J_n*58|J!|TakP_JkFxbl8B!3$u&(H360X5FUZm_uX?61!n zyPMvzc^~3DRbW2ctA~Bwip@9eKTybf0STd1vWi!~Ql6lH^fII6*S}#@LKtUYUdfQa zqLJokG03_2;7tSM`p)6*o{yu+5R6H%B*p_m$pN(c%wVg~s=6V_<-=cu5~x~HF~HN# z$kWq-aN*wT!(~SHd;I%&2JKZ-iZ)b-O4-26U?$W>@-Mjiji??8m z?%eASxi@}+5sx5(9Mu5WZQ(Y_ntT8`j-6(e4n^OI=nhDQxmP?}&;QZ#eb;J!se4;v zDyjb$aajkq@{%+B1>7*Rc_n4B0x73FC*-K(R>M|$HGlHIR+#n;saEk45Tya2Hzq0D zY0^;iyrsJUSeT*RLLA3a>q+vx5gWvm2{%$n4u)wrxF^J-BeU{v8{G9Wn^i60YER}$ znmH)$*Ktu6M;!QJkZC9x)@CG`E-M><^-%Jv!xxpGeNvL7Wr{?o6rTqEz>&^>xyz z1hxbxDZhH5v3l$N6jcT47RClF)g8`M^>xiGd|6~WpfWzKFAD?&io?=RBv*B!@}A1T0*OTpY|S=f84BI?Yt=CiR2E8@thtc z?Xq_rI}G2P6e+Ye-)p$mtWTq?Cynk|Bpav{cp0>j^S?_Q2iYKcuWv8fx0QzcQea2n z@V$q9th+Kk%y#){$85S+j#zLv`V(XPe!IWP!OqK&&vcc`xx3G~H|)$NLRO7dsxwIu zqUG4aN;PT3-e~G_XL2M=#Nfa_&_ca)337`ua`nyd$U z&+-j^;TU%xZ6ZOZjr7Vr{`RQ!VM|+S9e{cuVt>q~ss=Ad6Gpx@c=fF(jim=Z>{?YH z)~y?P2l@hf7%j1WG~pF*#JVI@I=e(Rd5$WHJ39jvDpRIFo*P-?BFWZYq|(3oLn*4B zoWCWu7BY`gL5o%~RU1oRHvOcNcYJ)|lX+0&v2Fq)i8{^5W!Y!xDWMD(QIU<%$>7M#Yy z9~}7;>{&iFda~@y4a(^2;9Su4oaXKGCD&=KQ3wOcZ|lFGH~EyM2W=lHe`tIFzc_mT z40_u=IqI%WiA%7I42GLvS~Q!=Q~`nA^_q$L3|3PeV_p;u#IY-w;f0U|Qpl^cf?OL2 zYW2I{nkvLGxw40lZX>I0tUB?9_+QA^U1gshLw4@&2w&I8O$(?w!ixsK>V#&ZGHa)Q z*cu=zCZpkP;R0dQCTJX%Uw69S4vFwYes5``guhAqZHvpxgvU9XxUQOqWWq^3FA%Vj zcHI|$qBk$dFfU-XDa-(OLaA@8_8#XSi!5~Qlh{ znS;{cRSvVk$h_6Gz>t!Uby`&NHvjqFOX;8Jo~nkzmQqdgt-J~#Tf3R-xz7S{Q~21T z^5ut2J-a}!Gg$N;bGJQj;Q{Do-q<~I)p}9e9nsPIMx3Jr4LMF@U)<-ZD@L^L zg{kD_PGsv4g8+?ATx3*xUiOEge|ecl4vYpum7JqD0aWY_xFt*c3g@P%VFSaT~tOo`ub9C1_uribA}{AL_e?+920w2e$Xq%bzxh(D(-6{g;mr| zIBmtIj8aeauW)ta*XHE**#EYuVh>eu{QJt*Yb?h!xj&ApH%4PsPv9A??#F=a4$)u4 zm#UQ#Gp$G1qqRNGwEN)b==>0i`USN~BFWmrwmvP4(IheYYFuzWg0*wejCW)(C3b@q z+9|WDs0!jO$iOD%So$JHY3OW)C}F=HfPoH-*J&+|piQS9>r9IzAta+=`WNIt55-SG z6Qw|biBfOi`4#s~)XrfRyVQNS2H#s|Y@S1D^ADVL9HXtqB|X9IdB;a0U#y=y4_oPg zio!XRXyv`a=R5Mk19y}|sSw1Y7V9kBBpf15BsqHREkT7Y{7socRIAi#=IR9nrdWf) z5SXTr;vJ9T3X>{QAdC}_lM0~!X1xDLOqCaoSGYcdbXdR34Z(9t8vv^Svb zYhdLX%LugOFELw=KXN<27rW45m9KJ2Mh_>eXJh2DfcR8LX2)I4gcGqtL= z{PO_HkRq!e)3Vk4IVRPJ{L4#9{+hendwtYAe4CS?ux-*Wf7%`nXzaq2=pbG@3{*;J zH`B1gQ{Uv4NyCC$&Xo)DJr&D#UJ*WogAn&`u()mn;3O&mpew>VgarIKVaa1N^-rn~ z)aYH(IUky?edhfGj|0;`uG%{?PYxV-j~P@w!tRzHiu(U90FU$pov^r-isB99pBlOm zf|Q9xR==|WAIIIIT{>quzd?8SrVQCw+aH|NJk+G%SW?Y4mncJC#Rp7TkJr1{+@L;b zcnsTcZS8x1CfhJg!Sot!Q{CocHhV;NQcti_=*QOj;>G)4OkLN2@kB(3`fd~rQ=x)$ zHpXM1bC7~7$mL=a54*&+tgU<{Ti2$UYh$SRj*)vPZU?R{seFl=1iG*KuI`R{$n302 z$mD$+0PzGUZ6C;qmsZ!>)_tcFrUb3XntG3$4W<56u!*-_Wh?o5RI_F~WyhX^8 z_0~H=`2y*x8~2}bl!{sTQXa$RjW^MfBWmr4yt?jikdEQBh8O;rR}9n}elaJ^Rlh_& z;Lza?{Sw&w3kF?O(IE@t*7i{kI zgNv7~=icLIX#stNkyBvjn>7nYyZ7LEsH)1+s^#?U-lekkdo|bBQO}jr7P;ig3<3=Q zAt3U@Cx4~5**5M;+l0=Vn+1(W&-S+7zmwSiNOLUp%`0c6qf;{)a#Qz$9ny<)5VMQt z6|$~Ocs4L{8;Qv%^h=0br*<1gB0n(e;<&@<-JlMqU}|c2raZgq;KG0C-I3?=sZmW> z)h|Fb@*_N+{hJ))OVolarPuGQ6m4&+D14>u{fk{MsNmFaA?=)F5fs@}4Cd&Wvo_&9 z1tSx4750;lXU1$hvj#MP<8EJJl2JmZ2K!eJa&=>Pe;By&9lz(?t+bLC3NYP4Q3>iI zM{5D>RUg~OTWe~c011uCdOz(hhDjj+O4M(9ztA1^!|B}tzx(o4bhe&(6zt#nde3&4 zKg3wX1WOnD?JP@9ML$ALwq)xiZw=H&Y+|1>0&mU9NB!`7y|Oa=)Ii;R=^$w@mr{F! zY6-YUd?d>tNt!8q+aoQ)^=Iq{OMN@uRS;pp|J6hjJ{O=;H3+Q{0VK!zoG#m7FIS0R z4G0ax{n%?fzPfk6`|BvuP3!OY&Ll@kg}AE*G9LSGbv8;(e<2@2ye=Tk2P($ih%-BrO*m=HfheL5xO&ls_8R*nDNXf6u;3KNNw{A8JWjP* zyE=kC)BoddJaq_rBXIFl8poti_I(NdOl#TkOmMSIk#02Y+-mCH3YAjf_D!8n?hiJV z<2(Ti@%F&{A}wvo_FtI87RQrYM${?&!$<8#UZ=%_?coPr