From 68e3e0d2b29e5d094a1f405b43d4107e1ff18512 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 20 Aug 2024 13:55:50 +0200 Subject: [PATCH 01/57] Format file --- .../impl/notifications/factories/NotificationCreator.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index b1b4068efe..5dfbee4de0 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -446,10 +446,10 @@ class DefaultNotificationCreator @Inject constructor( ): MessagingStyle { return MessagingStyle( Person.Builder() - .setName(user.displayName?.annotateForDebug(50)) - .setIcon(bitmapLoader.getUserIcon(user.avatarUrl, imageLoader)) - .setKey(sessionId.value) - .build() + .setName(user.displayName?.annotateForDebug(50)) + .setIcon(bitmapLoader.getUserIcon(user.avatarUrl, imageLoader)) + .setKey(sessionId.value) + .build() ).also { it.conversationTitle = roomName.takeIf { roomIsGroup } it.isGroupConversation = roomIsGroup From 19bca0775a53422dad415d1bf81c73257957257c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 20 Aug 2024 14:00:06 +0200 Subject: [PATCH 02/57] Remove usage of `with(notificationDataFactory)` for code clarity. --- .../notifications/NotificationRenderer.kt | 116 +++++++++--------- 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index e36966cc9a..181a1bd364 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -42,77 +42,75 @@ class NotificationRenderer @Inject constructor( imageLoader: ImageLoader, ) { val groupedEvents = eventsToProcess.groupByType() - with(notificationDataFactory) { - val roomNotifications = toNotifications(groupedEvents.roomEvents, currentUser, imageLoader) - val invitationNotifications = toNotifications(groupedEvents.invitationEvents) - val simpleNotifications = toNotifications(groupedEvents.simpleEvents) - val fallbackNotifications = toNotifications(groupedEvents.fallbackEvents) - val summaryNotification = createSummaryNotification( - currentUser = currentUser, - roomNotifications = roomNotifications, - invitationNotifications = invitationNotifications, - simpleNotifications = simpleNotifications, - fallbackNotifications = fallbackNotifications, + val roomNotifications = notificationDataFactory.toNotifications(groupedEvents.roomEvents, currentUser, imageLoader) + val invitationNotifications = notificationDataFactory.toNotifications(groupedEvents.invitationEvents) + val simpleNotifications = notificationDataFactory.toNotifications(groupedEvents.simpleEvents) + val fallbackNotifications = notificationDataFactory.toNotifications(groupedEvents.fallbackEvents) + val summaryNotification = notificationDataFactory.createSummaryNotification( + currentUser = currentUser, + roomNotifications = roomNotifications, + invitationNotifications = invitationNotifications, + simpleNotifications = simpleNotifications, + fallbackNotifications = fallbackNotifications, + ) + + // Remove summary first to avoid briefly displaying it after dismissing the last notification + if (summaryNotification == SummaryNotification.Removed) { + Timber.tag(loggerTag.value).d("Removing summary notification") + notificationDisplayer.cancelNotificationMessage( + tag = null, + id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId) ) + } - // Remove summary first to avoid briefly displaying it after dismissing the last notification - if (summaryNotification == SummaryNotification.Removed) { - Timber.tag(loggerTag.value).d("Removing summary notification") - notificationDisplayer.cancelNotificationMessage( - tag = null, - id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId) - ) - } + roomNotifications.forEach { notificationData -> + notificationDisplayer.showNotificationMessage( + tag = notificationData.roomId.value, + id = NotificationIdProvider.getRoomMessagesNotificationId(currentUser.userId), + notification = notificationData.notification + ) + } - roomNotifications.forEach { notificationData -> + invitationNotifications.forEach { notificationData -> + if (useCompleteNotificationFormat) { + Timber.tag(loggerTag.value).d("Updating invitation notification ${notificationData.key}") notificationDisplayer.showNotificationMessage( - tag = notificationData.roomId.value, - id = NotificationIdProvider.getRoomMessagesNotificationId(currentUser.userId), + tag = notificationData.key, + id = NotificationIdProvider.getRoomInvitationNotificationId(currentUser.userId), notification = notificationData.notification ) } + } - invitationNotifications.forEach { notificationData -> - if (useCompleteNotificationFormat) { - Timber.tag(loggerTag.value).d("Updating invitation notification ${notificationData.key}") - notificationDisplayer.showNotificationMessage( - tag = notificationData.key, - id = NotificationIdProvider.getRoomInvitationNotificationId(currentUser.userId), - notification = notificationData.notification - ) - } - } - - simpleNotifications.forEach { notificationData -> - if (useCompleteNotificationFormat) { - Timber.tag(loggerTag.value).d("Updating simple notification ${notificationData.key}") - notificationDisplayer.showNotificationMessage( - tag = notificationData.key, - id = NotificationIdProvider.getRoomEventNotificationId(currentUser.userId), - notification = notificationData.notification - ) - } - } - - // Show only the first fallback notification - if (fallbackNotifications.isNotEmpty()) { - Timber.tag(loggerTag.value).d("Showing fallback notification") + simpleNotifications.forEach { notificationData -> + if (useCompleteNotificationFormat) { + Timber.tag(loggerTag.value).d("Updating simple notification ${notificationData.key}") notificationDisplayer.showNotificationMessage( - tag = "FALLBACK", - id = NotificationIdProvider.getFallbackNotificationId(currentUser.userId), - notification = fallbackNotifications.first().notification + tag = notificationData.key, + id = NotificationIdProvider.getRoomEventNotificationId(currentUser.userId), + notification = notificationData.notification ) } + } - // Update summary last to avoid briefly displaying it before other notifications - if (summaryNotification is SummaryNotification.Update) { - Timber.tag(loggerTag.value).d("Updating summary notification") - notificationDisplayer.showNotificationMessage( - tag = null, - id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId), - notification = summaryNotification.notification - ) - } + // Show only the first fallback notification + if (fallbackNotifications.isNotEmpty()) { + Timber.tag(loggerTag.value).d("Showing fallback notification") + notificationDisplayer.showNotificationMessage( + tag = "FALLBACK", + id = NotificationIdProvider.getFallbackNotificationId(currentUser.userId), + notification = fallbackNotifications.first().notification + ) + } + + // Update summary last to avoid briefly displaying it before other notifications + if (summaryNotification is SummaryNotification.Update) { + Timber.tag(loggerTag.value).d("Updating summary notification") + notificationDisplayer.showNotificationMessage( + tag = null, + id = NotificationIdProvider.getSummaryNotificationId(currentUser.userId), + notification = summaryNotification.notification + ) } } } From 43d619217cf26446e9ecd37af6aae8f894177609 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 20 Aug 2024 14:04:20 +0200 Subject: [PATCH 03/57] NotifiableEvent does not need to be Serializable anymore. --- .../libraries/push/impl/notifications/model/NotifiableEvent.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableEvent.kt index ddfbbf8b07..282382eaee 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableEvent.kt @@ -19,12 +19,11 @@ package io.element.android.libraries.push.impl.notifications.model 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 java.io.Serializable /** * Parent interface for all events which can be displayed as a Notification. */ -sealed interface NotifiableEvent : Serializable { +sealed interface NotifiableEvent { val sessionId: SessionId val roomId: RoomId val eventId: EventId From d867a5fe6fd492e12e0c50b8a8aa0f87de48bb63 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 20 Aug 2024 15:14:49 +0200 Subject: [PATCH 04/57] Cleanup notification for redacted event. --- .../api/notification/NotificationData.kt | 4 +- ...imelineEventToNotificationContentMapper.kt | 6 +- .../android/libraries/matrix/test/TestData.kt | 2 + .../DefaultNotifiableEventResolver.kt | 35 +- .../factories/NotificationCreator.kt | 5 + .../notifications/model/ResolvedPushEvent.kt | 32 ++ .../push/impl/push/DefaultPushHandler.kt | 29 +- .../push/impl/push/OnRedactedEventReceived.kt | 97 ++++++ .../DefaultNotifiableEventResolverTest.kt | 307 +++++++++++------- .../FakeNotifiableEventResolver.kt | 6 +- .../push/impl/push/DefaultPushHandlerTest.kt | 68 +++- .../impl/push/FakeOnRedactedEventReceived.kt | 28 ++ 12 files changed, 466 insertions(+), 153 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/ResolvedPushEvent.kt create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnRedactedEventReceived.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 74bd2d8e14..b1dd0c683c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -79,8 +79,8 @@ sealed interface NotificationContent { ) : MessageLike data class RoomRedaction( - val redactedEventId: String?, - val reason: String? + val redactedEventId: EventId?, + val reason: String?, ) : MessageLike data object Sticker : MessageLike diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt index fa0b6365f3..39dadffe59 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.notification +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.notification.CallNotifyType import io.element.android.libraries.matrix.api.notification.NotificationContent @@ -94,7 +95,10 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon is MessageLikeEventContent.RoomMessage -> { NotificationContent.MessageLike.RoomMessage(senderId, EventMessageMapper().mapMessageType(messageType)) } - is MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction(redactedEventId = redactedEventId, reason = reason) + is MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction( + redactedEventId = redactedEventId?.let(::EventId), + reason = reason, + ) MessageLikeEventContent.Sticker -> NotificationContent.MessageLike.Sticker is MessageLikeEventContent.Poll -> NotificationContent.MessageLike.Poll(senderId, question) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index a84fe1edf6..3b39530978 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -63,6 +63,8 @@ const val A_MESSAGE = "Hello world!" const val A_REPLY = "OK, I'll be there!" const val ANOTHER_MESSAGE = "Hello universe!" +const val A_REDACTION_REASON = "A redaction reason" + const val A_HOMESERVER_URL = "matrix.org" const val A_HOMESERVER_URL_2 = "matrix-client.org" diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt index ef97dbf002..7dd9ef9d9d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt @@ -50,8 +50,8 @@ import io.element.android.libraries.matrix.ui.messages.toPlainText import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent -import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.toolbox.api.strings.StringProvider import io.element.android.services.toolbox.api.systemclock.SystemClock @@ -67,7 +67,7 @@ private val loggerTag = LoggerTag("DefaultNotifiableEventResolver", LoggerTag.No * this pattern allow decoupling between the object responsible of displaying notifications and the matrix sdk. */ interface NotifiableEventResolver { - suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? + suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): ResolvedPushEvent? } @ContributesBinding(AppScope::class) @@ -80,7 +80,7 @@ class DefaultNotifiableEventResolver @Inject constructor( private val permalinkParser: PermalinkParser, private val callNotificationEventResolver: CallNotificationEventResolver, ) : NotifiableEventResolver { - override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { + override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): ResolvedPushEvent? { // Restore session val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null val notificationService = client.notificationService() @@ -99,8 +99,9 @@ class DefaultNotifiableEventResolver @Inject constructor( private suspend fun NotificationData.asNotifiableEvent( client: MatrixClient, userId: SessionId, - ): NotifiableEvent? { - return when (val content = this.content) { + ): ResolvedPushEvent? { + val content = this.content + val notifiableEvent = when (content) { is NotificationContent.MessageLike.RoomMessage -> { val senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId) val messageBody = descriptionFromMessageContent(content, senderDisambiguatedDisplayName) @@ -204,8 +205,9 @@ class DefaultNotifiableEventResolver @Inject constructor( NotificationContent.MessageLike.RoomEncrypted -> fallbackNotifiableEvent(userId, roomId, eventId).also { Timber.tag(loggerTag.value).w("Notification with encrypted content -> fallback") } - is NotificationContent.MessageLike.RoomRedaction -> null.also { - Timber.tag(loggerTag.value).d("Ignoring notification for redaction") + is NotificationContent.MessageLike.RoomRedaction -> { + // Note: this case will be handled below + null } NotificationContent.MessageLike.Sticker -> null.also { Timber.tag(loggerTag.value).d("Ignoring notification for sticker") @@ -233,6 +235,25 @@ class DefaultNotifiableEventResolver @Inject constructor( Timber.tag(loggerTag.value).d("Ignoring notification for state event ${content.javaClass.simpleName}") } } + + return if (notifiableEvent != null) { + ResolvedPushEvent.Event(notifiableEvent) + } else if (content is NotificationContent.MessageLike.RoomRedaction) { + val redactedEventId = content.redactedEventId + if (redactedEventId == null) { + Timber.tag(loggerTag.value).d("redactedEventId is null.") + null + } else { + ResolvedPushEvent.Redaction( + sessionId = userId, + roomId = roomId, + redactedEventId = redactedEventId, + reason = content.reason, + ) + } + } else { + null + } } private fun fallbackNotifiableEvent( diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 5dfbee4de0..096132ffab 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -430,6 +430,7 @@ class DefaultNotificationCreator @Inject constructor( event.imageUri?.let { message.setData("image/", it) } + message.extras.putString(MESSAGE_EVENT_ID, event.eventId.value) } addMessage(message) } @@ -465,6 +466,10 @@ class DefaultNotificationCreator @Inject constructor( drawable.draw(canvas) return bitmap } + + companion object { + const val MESSAGE_EVENT_ID = "message_event_id" + } } fun NotifiableMessageEvent.isSmartReplyError() = outGoingMessage && outGoingMessageFailed diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/ResolvedPushEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/ResolvedPushEvent.kt new file mode 100644 index 0000000000..9722d6c3f6 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/ResolvedPushEvent.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.notifications.model + +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 + +sealed interface ResolvedPushEvent { + data class Event(val notifiableEvent: NotifiableEvent) : ResolvedPushEvent + + data class Redaction( + val sessionId: SessionId, + val roomId: RoomId, + val redactedEventId: EventId, + val reason: String?, + ) : ResolvedPushEvent +} 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 a553c14952..5f33e3134a 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 @@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.libraries.push.impl.test.DefaultTestPush import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.pushproviders.api.PushData @@ -41,6 +42,7 @@ private val loggerTag = LoggerTag("PushHandler", LoggerTag.PushLoggerTag) @ContributesBinding(AppScope::class) class DefaultPushHandler @Inject constructor( private val onNotifiableEventReceived: OnNotifiableEventReceived, + private val onRedactedEventReceived: OnRedactedEventReceived, private val notifiableEventResolver: NotifiableEventResolver, private val incrementPushDataStore: IncrementPushDataStore, private val userPushStoreFactory: UserPushStoreFactory, @@ -96,19 +98,26 @@ class DefaultPushHandler @Inject constructor( Timber.w("Unable to get a session") return } - val notifiableEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId) - when (notifiableEvent) { + val resolvedPushEvent = notifiableEventResolver.resolveEvent(userId, pushData.roomId, pushData.eventId) + when (resolvedPushEvent) { null -> Timber.tag(loggerTag.value).w("Unable to get a notification data") - is NotifiableRingingCallEvent -> handleRingingCallEvent(notifiableEvent) - else -> { - val userPushStore = userPushStoreFactory.getOrCreate(userId) - val areNotificationsEnabled = userPushStore.getNotificationEnabledForDevice().first() - if (areNotificationsEnabled) { - onNotifiableEventReceived.onNotifiableEventReceived(notifiableEvent) - } else { - Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.") + is ResolvedPushEvent.Event -> { + when (resolvedPushEvent.notifiableEvent) { + is NotifiableRingingCallEvent -> handleRingingCallEvent(resolvedPushEvent.notifiableEvent) + else -> { + val userPushStore = userPushStoreFactory.getOrCreate(userId) + val areNotificationsEnabled = userPushStore.getNotificationEnabledForDevice().first() + if (areNotificationsEnabled) { + onNotifiableEventReceived.onNotifiableEventReceived(resolvedPushEvent.notifiableEvent) + } else { + Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.") + } + } } } + is ResolvedPushEvent.Redaction -> { + onRedactedEventReceived.onRedactedEventReceived(resolvedPushEvent) + } } } catch (e: Exception) { Timber.tag(loggerTag.value).e(e, "## handleInternal() failed") diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt new file mode 100644 index 0000000000..f632c2bbcc --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/OnRedactedEventReceived.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import android.content.Context +import android.graphics.Typeface +import android.text.style.StyleSpan +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.MessagingStyle +import androidx.core.text.buildSpannedString +import androidx.core.text.inSpans +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.push.impl.notifications.ActiveNotificationsProvider +import io.element.android.libraries.push.impl.notifications.NotificationDisplayer +import io.element.android.libraries.push.impl.notifications.factories.DefaultNotificationCreator +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.services.toolbox.api.strings.StringProvider +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +interface OnRedactedEventReceived { + fun onRedactedEventReceived(redaction: ResolvedPushEvent.Redaction) +} + +@ContributesBinding(AppScope::class) +class DefaultOnRedactedEventReceived @Inject constructor( + private val activeNotificationsProvider: ActiveNotificationsProvider, + private val notificationDisplayer: NotificationDisplayer, + private val coroutineScope: CoroutineScope, + @ApplicationContext private val context: Context, + private val stringProvider: StringProvider, +) : OnRedactedEventReceived { + override fun onRedactedEventReceived(redaction: ResolvedPushEvent.Redaction) { + coroutineScope.launch { + val notifications = activeNotificationsProvider.getMessageNotificationsForRoom( + redaction.sessionId, + redaction.roomId, + ) + if (notifications.isEmpty()) { + Timber.d("No notifications found for redacted event") + } + notifications.forEach { statusBarNotification -> + val notification = statusBarNotification.notification + val messagingStyle = MessagingStyle.extractMessagingStyleFromNotification(notification) + if (messagingStyle == null) { + Timber.w("Unable to retrieve messaging style from notification") + return@forEach + } + val messageToRedactIndex = messagingStyle.messages.indexOfFirst { message -> + message.extras.getString(DefaultNotificationCreator.MESSAGE_EVENT_ID) == redaction.redactedEventId.value + } + if (messageToRedactIndex == -1) { + Timber.d("Unable to find the message to remove from notification") + return@forEach + } + val oldMessage = messagingStyle.messages[messageToRedactIndex] + val content = buildSpannedString { + inSpans(StyleSpan(Typeface.ITALIC)) { + append(stringProvider.getString(CommonStrings.common_message_removed)) + } + } + val newMessage = MessagingStyle.Message( + content, + oldMessage.timestamp, + oldMessage.person + ) + messagingStyle.messages[messageToRedactIndex] = newMessage + notificationDisplayer.showNotificationMessage( + statusBarNotification.tag, + statusBarNotification.id, + NotificationCompat.Builder(context, notification) + .setStyle(messagingStyle) + .build() + ) + } + } + } +} diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index 0c0dbcc495..997d05da3d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageT 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.AN_EXCEPTION +import io.element.android.libraries.matrix.test.A_REDACTION_REASON 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_2 @@ -52,6 +53,7 @@ import io.element.android.libraries.push.impl.notifications.model.FallbackNotifi import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.services.toolbox.impl.strings.AndroidStringProvider import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP @@ -104,7 +106,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Hello world") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Hello world") + ) assertThat(result).isEqualTo(expectedResult) } @@ -123,7 +127,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Hello world", hasMentionOrReply = true) + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Hello world", hasMentionOrReply = true) + ) assertThat(result).isEqualTo(expectedResult) } @@ -146,7 +152,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Hello world") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Hello world") + ) assertThat(result).isEqualTo(expectedResult) } @@ -169,7 +177,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Hello world") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Hello world") + ) assertThat(result).isEqualTo(expectedResult) } @@ -186,7 +196,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Audio") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Audio") + ) assertThat(result).isEqualTo(expectedResult) } @@ -203,7 +215,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Video") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Video") + ) assertThat(result).isEqualTo(expectedResult) } @@ -220,7 +234,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Voice message") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Voice message") + ) assertThat(result).isEqualTo(expectedResult) } @@ -237,7 +253,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Image") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Image") + ) assertThat(result).isEqualTo(expectedResult) } @@ -254,7 +272,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Sticker") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Sticker") + ) assertThat(result).isEqualTo(expectedResult) } @@ -271,7 +291,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "File") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "File") + ) assertThat(result).isEqualTo(expectedResult) } @@ -288,7 +310,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Location") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Location") + ) assertThat(result).isEqualTo(expectedResult) } @@ -305,7 +329,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Notice") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Notice") + ) assertThat(result).isEqualTo(expectedResult) } @@ -322,7 +348,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "* Bob is happy") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "* Bob is happy") + ) assertThat(result).isEqualTo(expectedResult) } @@ -339,7 +367,9 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = createNotifiableMessageEvent(body = "Poll: A question") + val expectedResult = ResolvedPushEvent.Event( + createNotifiableMessageEvent(body = "Poll: A question") + ) assertThat(result).isEqualTo(expectedResult) } @@ -357,21 +387,23 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = InviteNotifiableEvent( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - canBeReplaced = true, - roomName = null, - noisy = false, - title = null, - description = "Invited you to join the room", - type = null, - timestamp = A_TIMESTAMP, - soundName = null, - isRedacted = false, - isUpdated = false, + val expectedResult = ResolvedPushEvent.Event( + InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = true, + roomName = null, + noisy = false, + title = null, + description = "Invited you to join the room", + type = null, + timestamp = A_TIMESTAMP, + soundName = null, + isRedacted = false, + isUpdated = false, + ) ) assertThat(result).isEqualTo(expectedResult) } @@ -390,21 +422,23 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = InviteNotifiableEvent( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - canBeReplaced = true, - roomName = null, - noisy = false, - title = null, - description = "Invited you to chat", - type = null, - timestamp = A_TIMESTAMP, - soundName = null, - isRedacted = false, - isUpdated = false, + val expectedResult = ResolvedPushEvent.Event( + InviteNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = true, + roomName = null, + noisy = false, + title = null, + description = "Invited you to chat", + type = null, + timestamp = A_TIMESTAMP, + soundName = null, + isRedacted = false, + isUpdated = false, + ) ) assertThat(result).isEqualTo(expectedResult) } @@ -435,16 +469,18 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = FallbackNotifiableEvent( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - description = "Notification", - canBeReplaced = true, - isRedacted = false, - isUpdated = false, - timestamp = A_FAKE_TIMESTAMP, + val expectedResult = ResolvedPushEvent.Event( + FallbackNotifiableEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + description = "Notification", + canBeReplaced = true, + isRedacted = false, + isUpdated = false, + timestamp = A_FAKE_TIMESTAMP, + ) ) assertThat(result).isEqualTo(expectedResult) } @@ -459,27 +495,29 @@ class DefaultNotifiableEventResolverTest { ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) - val expectedResult = NotifiableMessageEvent( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - canBeReplaced = false, - senderId = A_USER_ID_2, - noisy = false, - timestamp = A_TIMESTAMP, - senderDisambiguatedDisplayName = "Bob", - body = "Call in progress (unsupported)", - imageUriString = null, - threadId = null, - roomName = null, - roomAvatarPath = null, - senderAvatarPath = null, - soundName = null, - outGoingMessage = false, - outGoingMessageFailed = false, - isRedacted = false, - isUpdated = false + val expectedResult = ResolvedPushEvent.Event( + NotifiableMessageEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + canBeReplaced = false, + senderId = A_USER_ID_2, + noisy = false, + timestamp = A_TIMESTAMP, + senderDisambiguatedDisplayName = "Bob", + body = "Call in progress (unsupported)", + imageUriString = null, + threadId = null, + roomName = null, + roomAvatarPath = null, + senderAvatarPath = null, + soundName = null, + outGoingMessage = false, + outGoingMessageFailed = false, + isRedacted = false, + isUpdated = false + ) ) assertThat(result).isEqualTo(expectedResult) } @@ -498,21 +536,23 @@ class DefaultNotifiableEventResolverTest { ) ) ) - val expectedResult = NotifiableRingingCallEvent( - sessionId = A_SESSION_ID, - roomId = A_ROOM_ID, - eventId = AN_EVENT_ID, - senderId = A_USER_ID_2, - roomName = null, - editedEventId = null, - description = "Incoming call", - timestamp = timestamp, - canBeReplaced = true, - isRedacted = false, - isUpdated = false, - senderDisambiguatedDisplayName = "Bob", - senderAvatarUrl = null, - callNotifyType = CallNotifyType.RING, + val expectedResult = ResolvedPushEvent.Event( + NotifiableRingingCallEvent( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + eventId = AN_EVENT_ID, + senderId = A_USER_ID_2, + roomName = null, + editedEventId = null, + description = "Incoming call", + timestamp = timestamp, + canBeReplaced = true, + isRedacted = false, + isUpdated = false, + senderDisambiguatedDisplayName = "Bob", + senderAvatarUrl = null, + callNotifyType = CallNotifyType.RING, + ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) assertThat(result).isEqualTo(expectedResult) @@ -531,22 +571,24 @@ class DefaultNotifiableEventResolverTest { ) ) ) - val expectedResult = NotifiableMessageEvent( - sessionId = A_SESSION_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - noisy = true, - timestamp = 0L, - senderDisambiguatedDisplayName = "Bob", - senderId = UserId("@bob:server.org"), - body = "☎\uFE0F Incoming call", - roomId = A_ROOM_ID, - threadId = null, - roomName = null, - canBeReplaced = false, - isRedacted = false, - imageUriString = null, - type = EventType.CALL_NOTIFY, + val expectedResult = ResolvedPushEvent.Event( + NotifiableMessageEvent( + sessionId = A_SESSION_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = true, + timestamp = 0L, + senderDisambiguatedDisplayName = "Bob", + senderId = UserId("@bob:server.org"), + body = "☎\uFE0F Incoming call", + roomId = A_ROOM_ID, + threadId = null, + roomName = null, + canBeReplaced = false, + isRedacted = false, + imageUriString = null, + type = EventType.CALL_NOTIFY, + ) ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) assertThat(result).isEqualTo(expectedResult) @@ -564,22 +606,46 @@ class DefaultNotifiableEventResolverTest { ) ) ) - val expectedResult = NotifiableMessageEvent( + val expectedResult = ResolvedPushEvent.Event( + NotifiableMessageEvent( + sessionId = A_SESSION_ID, + eventId = AN_EVENT_ID, + editedEventId = null, + noisy = true, + timestamp = A_TIMESTAMP, + senderDisambiguatedDisplayName = "Bob", + senderId = UserId("@bob:server.org"), + body = "☎\uFE0F Incoming call", + roomId = A_ROOM_ID, + threadId = null, + roomName = null, + canBeReplaced = false, + isRedacted = false, + imageUriString = null, + type = EventType.CALL_NOTIFY, + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isEqualTo(expectedResult) + } + + @Test + fun `resolve RoomRedaction`() = runTest { + val sut = createDefaultNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomRedaction( + AN_EVENT_ID_2, + A_REDACTION_REASON, + ) + ) + ) + ) + val expectedResult = ResolvedPushEvent.Redaction( sessionId = A_SESSION_ID, - eventId = AN_EVENT_ID, - editedEventId = null, - noisy = true, - timestamp = A_TIMESTAMP, - senderDisambiguatedDisplayName = "Bob", - senderId = UserId("@bob:server.org"), - body = "☎\uFE0F Incoming call", roomId = A_ROOM_ID, - threadId = null, - roomName = null, - canBeReplaced = false, - isRedacted = false, - imageUriString = null, - type = EventType.CALL_NOTIFY, + redactedEventId = AN_EVENT_ID_2, + reason = A_REDACTION_REASON, ) val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) assertThat(result).isEqualTo(expectedResult) @@ -598,7 +664,6 @@ class DefaultNotifiableEventResolverTest { testNull(NotificationContent.MessageLike.KeyVerificationMac) testNull(NotificationContent.MessageLike.KeyVerificationDone) testNull(NotificationContent.MessageLike.ReactionContent(relatedEventId = AN_EVENT_ID_2.value)) - testNull(NotificationContent.MessageLike.RoomRedaction(redactedEventId = AN_EVENT_ID_2.value, reason = null)) testNull(NotificationContent.MessageLike.Sticker) testNull(NotificationContent.StateEvent.PolicyRuleRoom) testNull(NotificationContent.StateEvent.PolicyRuleServer) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt index a9ccabaa6d..234f2f11f7 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/FakeNotifiableEventResolver.kt @@ -19,13 +19,13 @@ package io.element.android.libraries.push.impl.notifications 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.push.impl.notifications.model.NotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.tests.testutils.lambda.lambdaError class FakeNotifiableEventResolver( - private val notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> lambdaError() } + private val notifiableEventResult: (SessionId, RoomId, EventId) -> ResolvedPushEvent? = { _, _, _ -> lambdaError() } ) : NotifiableEventResolver { - override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { + override suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): ResolvedPushEvent? { return notifiableEventResult(sessionId, roomId, eventId) } } 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 160f9f5135..2c287ee294 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 @@ -30,8 +30,10 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.CallNotifyType import io.element.android.libraries.matrix.api.timeline.item.event.EventType 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_ROOM_ID import io.element.android.libraries.matrix.test.A_SECRET +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.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -40,6 +42,7 @@ import io.element.android.libraries.push.impl.notifications.channels.FakeNotific import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent import io.element.android.libraries.push.impl.test.DefaultTestPush import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler import io.element.android.libraries.pushproviders.api.PushData @@ -61,7 +64,9 @@ class DefaultPushHandlerTest { fun `when classical PushData is received, the notification drawer is informed`() = runTest { val aNotifiableMessageEvent = aNotifiableMessageEvent() val notifiableEventResult = - lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + lambdaRecorder { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableMessageEvent) + } val onNotifiableEventReceived = lambdaRecorder {} val incrementPushCounterResult = lambdaRecorder {} val aPushData = PushData( @@ -94,7 +99,9 @@ class DefaultPushHandlerTest { runTest { val aNotifiableMessageEvent = aNotifiableMessageEvent() val notifiableEventResult = - lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + lambdaRecorder { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableMessageEvent) + } val onNotifiableEventReceived = lambdaRecorder {} val incrementPushCounterResult = lambdaRecorder {} val aPushData = PushData( @@ -128,7 +135,9 @@ class DefaultPushHandlerTest { runTest { val aNotifiableMessageEvent = aNotifiableMessageEvent() val notifiableEventResult = - lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + lambdaRecorder { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableMessageEvent) + } val onNotifiableEventReceived = lambdaRecorder {} val incrementPushCounterResult = lambdaRecorder {} val aPushData = PushData( @@ -164,7 +173,9 @@ class DefaultPushHandlerTest { runTest { val aNotifiableMessageEvent = aNotifiableMessageEvent() val notifiableEventResult = - lambdaRecorder { _, _, _ -> aNotifiableMessageEvent } + lambdaRecorder { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableMessageEvent) + } val onNotifiableEventReceived = lambdaRecorder {} val incrementPushCounterResult = lambdaRecorder {} val aPushData = PushData( @@ -197,7 +208,7 @@ class DefaultPushHandlerTest { fun `when classical PushData is received, but not able to resolve the event, nothing happen`() = runTest { val notifiableEventResult = - lambdaRecorder { _, _, _ -> null } + lambdaRecorder { _, _, _ -> null } val onNotifiableEventReceived = lambdaRecorder {} val incrementPushCounterResult = lambdaRecorder {} val aPushData = PushData( @@ -240,7 +251,9 @@ class DefaultPushHandlerTest { val elementCallEntryPoint = FakeElementCallEntryPoint(handleIncomingCallResult = handleIncomingCallLambda) val defaultPushHandler = createDefaultPushHandler( elementCallEntryPoint = elementCallEntryPoint, - notifiableEventResult = { _, _, _ -> aNotifiableCallEvent(callNotifyType = CallNotifyType.RING, timestamp = Instant.now().toEpochMilli()) }, + notifiableEventResult = { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableCallEvent(callNotifyType = CallNotifyType.RING, timestamp = Instant.now().toEpochMilli())) + }, incrementPushCounterResult = {}, pushClientSecret = FakePushClientSecret( getUserIdFromSecretResult = { A_USER_ID } @@ -265,7 +278,9 @@ class DefaultPushHandlerTest { val defaultPushHandler = createDefaultPushHandler( elementCallEntryPoint = elementCallEntryPoint, onNotifiableEventReceived = onNotifiableEventReceived, - notifiableEventResult = { _, _, _ -> aNotifiableMessageEvent(type = EventType.CALL_NOTIFY) }, + notifiableEventResult = { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableMessageEvent(type = EventType.CALL_NOTIFY)) + }, incrementPushCounterResult = {}, pushClientSecret = FakePushClientSecret( getUserIdFromSecretResult = { A_USER_ID } @@ -291,7 +306,9 @@ class DefaultPushHandlerTest { val defaultPushHandler = createDefaultPushHandler( elementCallEntryPoint = elementCallEntryPoint, onNotifiableEventReceived = onNotifiableEventReceived, - notifiableEventResult = { _, _, _ -> aNotifiableCallEvent() }, + notifiableEventResult = { _, _, _ -> + ResolvedPushEvent.Event(aNotifiableCallEvent()) + }, incrementPushCounterResult = {}, userPushStore = FakeUserPushStore().apply { setNotificationEnabledForDevice(false) @@ -305,6 +322,37 @@ class DefaultPushHandlerTest { onNotifiableEventReceived.assertions().isNeverCalled() } + @Test + fun `when a redaction is received, the onRedactedEventReceived is informed`() = runTest { + val aPushData = PushData( + eventId = AN_EVENT_ID, + roomId = A_ROOM_ID, + unread = 0, + clientSecret = A_SECRET, + ) + val aRedaction = ResolvedPushEvent.Redaction( + sessionId = A_SESSION_ID, + roomId = A_ROOM_ID, + redactedEventId = AN_EVENT_ID_2, + reason = null + ) + val onRedactedEventReceived = lambdaRecorder { } + val incrementPushCounterResult = lambdaRecorder {} + val defaultPushHandler = createDefaultPushHandler( + onRedactedEventReceived = onRedactedEventReceived, + incrementPushCounterResult = incrementPushCounterResult, + notifiableEventResult = { _, _, _ -> aRedaction }, + pushClientSecret = FakePushClientSecret( + getUserIdFromSecretResult = { A_USER_ID } + ), + ) + defaultPushHandler.handle(aPushData) + incrementPushCounterResult.assertions() + .isCalledOnce() + onRedactedEventReceived.assertions().isCalledOnce() + .with(value(aRedaction)) + } + @Test fun `when diagnostic PushData is received, the diagnostic push handler is informed`() = runTest { @@ -327,7 +375,8 @@ class DefaultPushHandlerTest { private fun createDefaultPushHandler( onNotifiableEventReceived: (NotifiableEvent) -> Unit = { lambdaError() }, - notifiableEventResult: (SessionId, RoomId, EventId) -> NotifiableEvent? = { _, _, _ -> lambdaError() }, + onRedactedEventReceived: (ResolvedPushEvent.Redaction) -> Unit = { lambdaError() }, + notifiableEventResult: (SessionId, RoomId, EventId) -> ResolvedPushEvent? = { _, _, _ -> lambdaError() }, incrementPushCounterResult: () -> Unit = { lambdaError() }, userPushStore: UserPushStore = FakeUserPushStore(), pushClientSecret: PushClientSecret = FakePushClientSecret(), @@ -339,6 +388,7 @@ class DefaultPushHandlerTest { ): DefaultPushHandler { return DefaultPushHandler( onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceived), + onRedactedEventReceived = FakeOnRedactedEventReceived(onRedactedEventReceived), notifiableEventResolver = FakeNotifiableEventResolver(notifiableEventResult), incrementPushDataStore = object : IncrementPushDataStore { override suspend fun incrementPushCounter() { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnRedactedEventReceived.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnRedactedEventReceived.kt new file mode 100644 index 0000000000..6a60243685 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/FakeOnRedactedEventReceived.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeOnRedactedEventReceived( + private val onRedactedEventReceivedResult: (ResolvedPushEvent.Redaction) -> Unit = { lambdaError() }, +) : OnRedactedEventReceived { + override fun onRedactedEventReceived(redaction: ResolvedPushEvent.Redaction) { + onRedactedEventReceivedResult(redaction) + } +} From 46106c96f72a916e1f437fdce01041ea99f7bd9b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2024 10:55:22 +0200 Subject: [PATCH 05/57] Remove unused method ActiveNotificationsProvider.getAllNotifications() --- .../ActiveNotificationsProvider.kt | 5 ----- .../DefaultActiveNotificationsProviderTest.kt | 21 ------------------- .../fake/FakeActiveNotificationsProvider.kt | 4 ---- 3 files changed, 30 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ActiveNotificationsProvider.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ActiveNotificationsProvider.kt index 01b9837f3b..c742c375d4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ActiveNotificationsProvider.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/ActiveNotificationsProvider.kt @@ -26,7 +26,6 @@ import io.element.android.libraries.push.api.notifications.NotificationIdProvide import javax.inject.Inject interface ActiveNotificationsProvider { - fun getAllNotifications(): List fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId): List fun getNotificationsForSession(sessionId: SessionId): List fun getMembershipNotificationForSession(sessionId: SessionId): List @@ -39,10 +38,6 @@ interface ActiveNotificationsProvider { class DefaultActiveNotificationsProvider @Inject constructor( private val notificationManager: NotificationManagerCompat, ) : ActiveNotificationsProvider { - override fun getAllNotifications(): List { - return notificationManager.activeNotifications - } - override fun getNotificationsForSession(sessionId: SessionId): List { return notificationManager.activeNotifications.filter { it.notification.group == sessionId.value } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt index 70403e1986..04c2a38a34 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultActiveNotificationsProviderTest.kt @@ -36,27 +36,6 @@ import org.robolectric.RobolectricTestRunner class DefaultActiveNotificationsProviderTest { private val notificationIdProvider = NotificationIdProvider - @Test - fun `getAllNotifications with no active notifications returns empty list`() { - val activeNotificationsProvider = createActiveNotificationsProvider(activeNotifications = emptyList()) - - val emptyNotifications = activeNotificationsProvider.getAllNotifications() - assertThat(emptyNotifications).isEmpty() - } - - @Test - fun `getAllNotifications with active notifications returns all`() { - val activeNotifications = listOf( - aStatusBarNotification(id = notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value), - aStatusBarNotification(id = notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value), - aStatusBarNotification(id = notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID), groupId = A_SESSION_ID.value), - ) - val activeNotificationsProvider = createActiveNotificationsProvider(activeNotifications = activeNotifications) - - val result = activeNotificationsProvider.getAllNotifications() - assertThat(result).hasSize(3) - } - @Test fun `getNotificationsForSession returns only notifications for that session id`() { val activeNotifications = listOf( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt index 680688d3dc..a5d306842f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt @@ -24,10 +24,6 @@ import io.element.android.libraries.push.impl.notifications.ActiveNotificationsP class FakeActiveNotificationsProvider( var activeNotifications: MutableList = mutableListOf(), ) : ActiveNotificationsProvider { - override fun getAllNotifications(): List { - return activeNotifications - } - override fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId): List { return activeNotifications } From d55bb57fa4da978dd58a97bec86a21bbf591fb88 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2024 11:06:26 +0200 Subject: [PATCH 06/57] Improve FakeActiveNotificationsProvider. --- .../DefaultNotificationDrawerManagerTest.kt | 7 ++++--- .../fake/FakeActiveNotificationsProvider.kt | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index bbe5e273b2..7d1e806911 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -72,7 +72,7 @@ class DefaultNotificationDrawerManagerTest { // For now just call all the API. Later, add more valuable tests. val matrixUser = aMatrixUser(id = A_SESSION_ID.value, displayName = "alice", avatarUrl = "mxc://data") val mockRoomGroupMessageCreator = FakeRoomGroupMessageCreator( - createRoomMessageResult = lambdaRecorder { user, _, roomId, _, existingNotification, -> + createRoomMessageResult = lambdaRecorder { user, _, roomId, _, existingNotification -> assertThat(user).isEqualTo(matrixUser) assertThat(roomId).isEqualTo(A_ROOM_ID) assertThat(existingNotification).isNull() @@ -167,11 +167,12 @@ class DefaultNotificationDrawerManagerTest { } val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID) val activeNotificationsProvider = FakeActiveNotificationsProvider( - mutableListOf( + getSummaryNotificationResult = { mockk { every { id } returns summaryId } - ) + }, + countResult = { 1 }, ) val defaultNotificationDrawerManager = createDefaultNotificationDrawerManager( notificationManager = notificationManager, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt index a5d306842f..d99a4264c0 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeActiveNotificationsProvider.kt @@ -22,29 +22,34 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.push.impl.notifications.ActiveNotificationsProvider class FakeActiveNotificationsProvider( - var activeNotifications: MutableList = mutableListOf(), + private val getMessageNotificationsForRoomResult: (SessionId, RoomId) -> List = { _, _ -> emptyList() }, + private val getNotificationsForSessionResult: (SessionId) -> List = { emptyList() }, + private val getMembershipNotificationForSessionResult: (SessionId) -> List = { emptyList() }, + private val getMembershipNotificationForRoomResult: (SessionId, RoomId) -> List = { _, _ -> emptyList() }, + private val getSummaryNotificationResult: (SessionId) -> StatusBarNotification? = { null }, + private val countResult: (SessionId) -> Int = { 0 }, ) : ActiveNotificationsProvider { override fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId): List { - return activeNotifications + return getMessageNotificationsForRoomResult(sessionId, roomId) } override fun getNotificationsForSession(sessionId: SessionId): List { - return activeNotifications + return getNotificationsForSessionResult(sessionId) } override fun getMembershipNotificationForSession(sessionId: SessionId): List { - return activeNotifications + return getMembershipNotificationForSessionResult(sessionId) } override fun getMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId): List { - return activeNotifications + return getMembershipNotificationForRoomResult(sessionId, roomId) } override fun getSummaryNotification(sessionId: SessionId): StatusBarNotification? { - return activeNotifications.firstOrNull() + return getSummaryNotificationResult(sessionId) } override fun count(sessionId: SessionId): Int { - return activeNotifications.size + return countResult(sessionId) } } From ea2957797a8851d25749237a57cff2b27c7e293d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2024 11:12:22 +0200 Subject: [PATCH 07/57] Add test on DefaultOnRedactedEventReceived --- .../DefaultOnRedactedEventReceivedTest.kt | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt new file mode 100644 index 0000000000..a038634f96 --- /dev/null +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.push + +import android.service.notification.StatusBarNotification +import androidx.test.platform.app.InstrumentationRegistry +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.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.push.impl.notifications.fake.FakeActiveNotificationsProvider +import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer +import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent +import io.element.android.services.toolbox.test.strings.FakeStringProvider +import io.element.android.tests.testutils.lambda.lambdaError +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class DefaultOnRedactedEventReceivedTest { + @Test + fun `when no notifications are found, nothing happen`() = runTest { + val sut = createDefaultOnRedactedEventReceived( + getMessageNotificationsForRoomResult = { _, _ -> emptyList() } + ) + sut.onRedactedEventReceived(ResolvedPushEvent.Redaction(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, null)) + } + + @Test + fun `when a notification is found, try to retrieve the message`() = runTest { + val sut = createDefaultOnRedactedEventReceived( + getMessageNotificationsForRoomResult = { _, _ -> + listOf( + mockk { + every { notification } returns mockk {} + } + ) + } + ) + sut.onRedactedEventReceived(ResolvedPushEvent.Redaction(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID, null)) + } + + private fun TestScope.createDefaultOnRedactedEventReceived( + getMessageNotificationsForRoomResult: (SessionId, RoomId) -> List = { _, _ -> lambdaError() }, + ): DefaultOnRedactedEventReceived { + val context = InstrumentationRegistry.getInstrumentation().context + return DefaultOnRedactedEventReceived( + activeNotificationsProvider = FakeActiveNotificationsProvider( + getMessageNotificationsForRoomResult = getMessageNotificationsForRoomResult, + getNotificationsForSessionResult = { lambdaError() }, + getMembershipNotificationForSessionResult = { lambdaError() }, + getMembershipNotificationForRoomResult = { _, _ -> lambdaError() }, + getSummaryNotificationResult = { lambdaError() }, + countResult = { lambdaError() }, + ), + notificationDisplayer = FakeNotificationDisplayer( + + ), + coroutineScope = this, + context = context, + stringProvider = FakeStringProvider(), + ) + } +} From e524c6cc64811c541440a6d01c6745336a2b8c90 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2024 11:32:12 +0200 Subject: [PATCH 08/57] Add missing test on null redacted eventId. --- .../DefaultNotifiableEventResolverTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt index 997d05da3d..2623bc8741 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt @@ -651,6 +651,22 @@ class DefaultNotifiableEventResolverTest { assertThat(result).isEqualTo(expectedResult) } + @Test + fun `resolve RoomRedaction with null redactedEventId should return null`() = runTest { + val sut = createDefaultNotifiableEventResolver( + notificationResult = Result.success( + createNotificationData( + content = NotificationContent.MessageLike.RoomRedaction( + null, + A_REDACTION_REASON, + ) + ) + ) + ) + val result = sut.resolveEvent(A_SESSION_ID, A_ROOM_ID, AN_EVENT_ID) + assertThat(result).isNull() + } + @Test fun `resolve null cases`() { testNull(NotificationContent.MessageLike.CallAnswer) From 61c66073e6c1b80de022b5cfe497439a45e3fdda Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2024 15:43:42 +0200 Subject: [PATCH 09/57] Fix formatting error --- .../push/impl/push/DefaultOnRedactedEventReceivedTest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt index a038634f96..bb2f318467 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/DefaultOnRedactedEventReceivedTest.kt @@ -73,9 +73,7 @@ class DefaultOnRedactedEventReceivedTest { getSummaryNotificationResult = { lambdaError() }, countResult = { lambdaError() }, ), - notificationDisplayer = FakeNotificationDisplayer( - - ), + notificationDisplayer = FakeNotificationDisplayer(), coroutineScope = this, context = context, stringProvider = FakeStringProvider(), From 1101c8db652cb09f63338671ab694d7b84a6cd7d Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 28 Aug 2024 16:47:56 +0200 Subject: [PATCH 10/57] Fix maplibre annotation plugins dependency --- gradle/libs.versions.toml | 1 + libraries/maplibre-compose/build.gradle.kts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81f93b1c0e..d6b552feff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -180,6 +180,7 @@ statemachine = "com.freeletics.flowredux:compose:1.2.2" maplibre = "org.maplibre.gl:android-sdk:11.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.0" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.0" +mapbox_android_gestures = "com.mapbox.mapboxsdk:mapbox-android-gestures:0.7.0" opusencoder = "io.element.android:opusencoder:1.1.0" kotlinpoet = "com.squareup:kotlinpoet:1.18.1" zxing_cpp = "io.github.zxing-cpp:android:2.2.0" diff --git a/libraries/maplibre-compose/build.gradle.kts b/libraries/maplibre-compose/build.gradle.kts index e2a9b821ba..b542f853b6 100644 --- a/libraries/maplibre-compose/build.gradle.kts +++ b/libraries/maplibre-compose/build.gradle.kts @@ -31,4 +31,6 @@ dependencies { api(libs.maplibre) api(libs.maplibre.ktx) api(libs.maplibre.annotation) + // needed for libs.maplibre.annotation waiting for a new release with the fix + implementation(libs.mapbox.android.gestures) } From 475a3a2861f47aa8c738a22c716e1d165ac68f92 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 28 Aug 2024 17:01:36 +0200 Subject: [PATCH 11/57] Release : use a different concurrency group for enterprise build. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fea2b6715c..ab99509722 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} concurrency: - group: ${{ format('build-release-main-gplay-{0}', github.sha) }} + group: ${{ format('build-release-main-enterprise-{0}', github.sha) }} cancel-in-progress: true steps: - uses: actions/checkout@v4 From 004679cbc47788ba4f918d1cbe2cd364623854a2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 28 Aug 2024 17:57:49 +0200 Subject: [PATCH 12/57] Changelog for version 0.5.1 --- CHANGES.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 2661597461..4382c8f975 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,92 @@ +Changes in Element X v0.5.1 (2024-08-28) +========================================= + +### ✨ Features +* Add simplified sliding sync toggle to developer options by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3222 +* Feature: identity reset by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3298 +* Timeline UI | MessageShield Support by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/3240 +* Suggestion for room alias (disabled for now) by @bmarty in https://github.com/element-hq/element-x-android/pull/3322 +* Allow `PictureInPicture` mode for Element Call. by @bmarty in https://github.com/element-hq/element-x-android/pull/3345 + +### 🙌 Improvements +* Join Room : allow to join by alias (and getPreview) by @ganfra in https://github.com/element-hq/element-x-android/pull/3241 +* [Feature] Pinned message : render m.room.pinned events in timeline by @ganfra in https://github.com/element-hq/element-x-android/pull/3276 +* Enable sync on push feature flag to partially sync when notifications arrive by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3290 +* Improve the text for mentions and replies in notifications by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3328 +* Use new functions exposed by Element Call about PiP by @bmarty in https://github.com/element-hq/element-x-android/pull/3334 + +### 🐛 Bugfixes +* Ensure sessionPath is not reused for different homeserver. Fixes not loading media issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/3299 +* Fix reset identity with password stuck in loading state. by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3317 + +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3252 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3267 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3297 +* Sync Strings - New language: Dutch. by @ElementBot in https://github.com/element-hq/element-x-android/pull/3308 +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/3339 + +### 🧱 Build +* Update sonarcloud project key by @guillaumevillemont in https://github.com/element-hq/element-x-android/pull/3264 +* Fix `build_rust_sdk.sh` script to work on linux by @erikjohnston in https://github.com/element-hq/element-x-android/pull/3291 +* Fix proguard config for nightly and release builds by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3294 +* Gradle update action: Use JDK 17 and skip early in forks. by @bmarty in https://github.com/element-hq/element-x-android/pull/3311 +* Gradle update action: add label and use other token. by @bmarty in https://github.com/element-hq/element-x-android/pull/3313 +* Update Gradle Wrapper from 8.9 to 8.10 by @ElementBot in https://github.com/element-hq/element-x-android/pull/3314 + +### 🚧 In development 🚧 +* WIP Pinned events : add feature flag and pin/unpin actions by @ganfra in https://github.com/element-hq/element-x-android/pull/3255 +* WIP Pinned events : start creating the banner ui, no logic. by @ganfra in https://github.com/element-hq/element-x-android/pull/3259 +* WIP Pinned events : banner logic by @ganfra in https://github.com/element-hq/element-x-android/pull/3275 + +### Dependency upgrades +* Update dependency org.maplibre.gl:android-sdk to v11.1.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3244 +* Update activity to v1.9.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3242 +* Update media3 to v1.4.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3247 +* Update dependency androidx.annotation:annotation-jvm to v1.8.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3243 +* Update dependencyAnalysis to v1.33.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3250 +* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.35 by @renovate in https://github.com/element-hq/element-x-android/pull/3249 +* Update dependency io.sentry:sentry-android to v7.12.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3246 +* Update dependency io.nlopez.compose.rules:detekt to v0.4.8 by @renovate in https://github.com/element-hq/element-x-android/pull/3254 +* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.36 by @renovate in https://github.com/element-hq/element-x-android/pull/3269 +* Update wysiwyg to v2.37.8 by @renovate in https://github.com/element-hq/element-x-android/pull/3263 +* Update dependency io.sentry:sentry-android to v7.13.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3258 +* Update dependency io.nlopez.compose.rules:detekt to v0.4.9 by @renovate in https://github.com/element-hq/element-x-android/pull/3277 +* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.38 by @renovate in https://github.com/element-hq/element-x-android/pull/3280 +* Update dependency androidx.annotation:annotation-jvm to v1.8.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3282 +* Update kotlin by @renovate in https://github.com/element-hq/element-x-android/pull/2990 +* Update dependency io.nlopez.compose.rules:detekt to v0.4.10 by @renovate in https://github.com/element-hq/element-x-android/pull/3281 +* Update dependency com.posthog:posthog-android to v3.5.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3287 +* Update wysiwyg to v2.37.8 by @renovate in https://github.com/element-hq/element-x-android/pull/3284 +* Update the SDK bindings to `v0.2.39` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/3288 +* Update gradle/actions action to v4 by @renovate in https://github.com/element-hq/element-x-android/pull/3265 +* Update android.gradle.plugin to v8.5.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3004 +* Update dependency io.sentry:sentry-android to v7.13.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3285 +* Update dependency io.sentry:sentry-android to v7.14.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3302 +* Update dependency androidx.test:runner to v1.6.2 by @renovate in https://github.com/element-hq/element-x-android/pull/3304 +* Update dependency com.otaliastudios:transcoder to v0.11.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3306 +* Update lifecycle to v2.8.0 by @renovate in https://github.com/element-hq/element-x-android/pull/2848 +* Update lifecycle to v2.8.4 by @renovate in https://github.com/element-hq/element-x-android/pull/3315 +* Update dagger to v2.52 by @renovate in https://github.com/element-hq/element-x-android/pull/3270 +* Update telephoto to v0.13.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3325 +* Update dependency androidx.compose:compose-bom to v2024.08.00 by @renovate in https://github.com/element-hq/element-x-android/pull/3323 +* Update dependency com.google.firebase:firebase-bom to v33.2.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3331 +* Update dependency com.posthog:posthog-android to v3.5.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3340 +* Update dependency com.android.tools:desugar_jdk_libs to v2.1.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3341 +* Update dependencyAnalysis to v2 (major) by @renovate in https://github.com/element-hq/element-x-android/pull/3346 +* Update dependency org.maplibre.gl:android-sdk to v11.2.0 by @renovate in https://github.com/element-hq/element-x-android/pull/3347 +* Update media3 to v1.4.1 by @renovate in https://github.com/element-hq/element-x-android/pull/3344 +* Update dependency org.matrix.rustcomponents:sdk-android to v0.2.40 by @renovate in https://github.com/element-hq/element-x-android/pull/3343 + +### Others +* Feature/fga/push subscribe to room by @ganfra in https://github.com/element-hq/element-x-android/pull/3257 +* Feature/fga/start sync on push by @ganfra in https://github.com/element-hq/element-x-android/pull/3260 +* Cleanup and add unit test for DefaultPinnedMessagesBannerFormatter by @bmarty in https://github.com/element-hq/element-x-android/pull/3307 +* Add test on function name which may start or end with spaces by @bmarty in https://github.com/element-hq/element-x-android/pull/3318 +* Fix broken direct room member for rooms with old users that left by @networkException in https://github.com/element-hq/element-x-android/pull/3324 +* Add unit test on MatrixRoom extension by @bmarty in https://github.com/element-hq/element-x-android/pull/3327 +* Fix login navigation getting stuck when the app was compiled with no-op analytics provider by @SpiritCroc in https://github.com/element-hq/element-x-android/pull/3337 + Changes in Element X v0.5.0 (2024-07-24) ========================================= From 8e7d8175d7e7cdbba6feef0bf2ad6825d5b70564 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Aug 2024 16:55:28 +0200 Subject: [PATCH 13/57] Remove `Lost your recovery key?` button and the temporary screen it opened. --- .../api/SecureBackupEntryPoint.kt | 3 - .../securebackup/impl/SecureBackupFlowNode.kt | 12 --- .../createkey/CreateNewRecoveryKeyNode.kt | 44 --------- .../createkey/CreateNewRecoveryKeyView.kt | 95 ------------------- .../enter/SecureBackupEnterRecoveryKeyNode.kt | 2 - .../enter/SecureBackupEnterRecoveryKeyView.kt | 12 +-- .../SecureBackupEnterRecoveryKeyViewTest.kt | 14 --- 7 files changed, 1 insertion(+), 181 deletions(-) delete mode 100644 features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt delete mode 100644 features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt diff --git a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt index 416ecff1bc..ab0d980a6e 100644 --- a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt +++ b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt @@ -32,9 +32,6 @@ interface SecureBackupEntryPoint : FeatureEntryPoint { @Parcelize data object EnterRecoveryKey : InitialTarget - @Parcelize - data object CreateNewRecoveryKey : InitialTarget - @Parcelize data object ResetIdentity : InitialTarget } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index d741eb11a7..19a10aa47a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -30,7 +30,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.securebackup.api.SecureBackupEntryPoint -import io.element.android.features.securebackup.impl.createkey.CreateNewRecoveryKeyNode import io.element.android.features.securebackup.impl.disable.SecureBackupDisableNode import io.element.android.features.securebackup.impl.enable.SecureBackupEnableNode import io.element.android.features.securebackup.impl.enter.SecureBackupEnterRecoveryKeyNode @@ -52,7 +51,6 @@ class SecureBackupFlowNode @AssistedInject constructor( initialElement = when (plugins.filterIsInstance().first().initialElement) { SecureBackupEntryPoint.InitialTarget.Root -> NavTarget.Root SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey -> NavTarget.EnterRecoveryKey - SecureBackupEntryPoint.InitialTarget.CreateNewRecoveryKey -> NavTarget.CreateNewRecoveryKey is SecureBackupEntryPoint.InitialTarget.ResetIdentity -> NavTarget.ResetIdentity }, savedStateMap = buildContext.savedStateMap, @@ -79,9 +77,6 @@ class SecureBackupFlowNode @AssistedInject constructor( @Parcelize data object EnterRecoveryKey : NavTarget - @Parcelize - data object CreateNewRecoveryKey : NavTarget - @Parcelize data object ResetIdentity : NavTarget } @@ -141,16 +136,9 @@ class SecureBackupFlowNode @AssistedInject constructor( backstack.pop() } } - - override fun onCreateNewRecoveryKey() { - backstack.push(NavTarget.CreateNewRecoveryKey) - } } createNode(buildContext, plugins = listOf(callback)) } - NavTarget.CreateNewRecoveryKey -> { - createNode(buildContext) - } is NavTarget.ResetIdentity -> { val callback = object : ResetIdentityFlowNode.Callback { override fun onDone() { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt deleted file mode 100644 index f1fbc6fe7e..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyNode.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2024 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.features.securebackup.impl.createkey - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import io.element.android.anvilannotations.ContributesNode -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.di.SessionScope - -@ContributesNode(SessionScope::class) -class CreateNewRecoveryKeyNode @AssistedInject constructor( - @Assisted buildContext: BuildContext, - @Assisted plugins: List, - private val buildMeta: BuildMeta, -) : Node(buildContext, plugins = plugins) { - @Composable - override fun View(modifier: Modifier) { - CreateNewRecoveryKeyView( - desktopApplicationName = buildMeta.desktopApplicationName, - modifier = modifier, - onBackClick = ::navigateUp, - ) - } -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt deleted file mode 100644 index ab19c0b522..0000000000 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/createkey/CreateNewRecoveryKeyView.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2024 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.features.securebackup.impl.createkey - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -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.text.AnnotatedString -import androidx.compose.ui.unit.dp -import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.securebackup.impl.R -import io.element.android.libraries.designsystem.atomic.organisms.NumberedListOrganism -import io.element.android.libraries.designsystem.components.BigIcon -import io.element.android.libraries.designsystem.components.PageTitle -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.Scaffold -import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.annotatedTextWithBold -import kotlinx.collections.immutable.toImmutableList - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CreateNewRecoveryKeyView( - desktopApplicationName: String, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Scaffold( - modifier = modifier, - topBar = { - TopAppBar(title = {}, navigationIcon = { BackButton(onClick = onBackClick) }) - } - ) { padding -> - Column( - modifier = Modifier.padding(padding) - ) { - PageTitle( - modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 40.dp), - title = stringResource(R.string.screen_create_new_recovery_key_title), - iconStyle = BigIcon.Style.Default(CompoundIcons.Computer()) - ) - Content(desktopApplicationName = desktopApplicationName) - } - } -} - -@Composable -private fun Content(desktopApplicationName: String) { - val listItems = buildList { - add(AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_1, desktopApplicationName))) - add(AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_2))) - add( - annotatedTextWithBold( - text = stringResource( - id = R.string.screen_create_new_recovery_key_list_item_3, - stringResource(R.string.screen_create_new_recovery_key_list_item_3_reset_all) - ), - boldText = stringResource(R.string.screen_create_new_recovery_key_list_item_3_reset_all) - ) - ) - add(AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_4))) - add(AnnotatedString(stringResource(R.string.screen_create_new_recovery_key_list_item_5))) - } - NumberedListOrganism(modifier = Modifier.padding(horizontal = 16.dp), items = listItems.toImmutableList()) -} - -@PreviewsDayNight -@Composable -internal fun CreateNewRecoveryKeyViewPreview() { - ElementPreview { - CreateNewRecoveryKeyView( - desktopApplicationName = "Element", - onBackClick = {}, - ) - } -} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt index 2fd9067f78..e54f6981c9 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt @@ -35,7 +35,6 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun onEnterRecoveryKeySuccess() - fun onCreateNewRecoveryKey() } private val callback = plugins().first() @@ -48,7 +47,6 @@ class SecureBackupEnterRecoveryKeyNode @AssistedInject constructor( modifier = modifier, onSuccess = callback::onEnterRecoveryKeySuccess, onBackClick = ::navigateUp, - onCreateNewRecoveryKey = callback::onCreateNewRecoveryKey ) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt index 952bfaad5f..cafaef7c82 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyView.kt @@ -33,7 +33,6 @@ import io.element.android.libraries.designsystem.components.async.AsyncActionVie 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.ui.strings.CommonStrings @Composable @@ -41,7 +40,6 @@ fun SecureBackupEnterRecoveryKeyView( state: SecureBackupEnterRecoveryKeyState, onSuccess: () -> Unit, onBackClick: () -> Unit, - onCreateNewRecoveryKey: () -> Unit, modifier: Modifier = Modifier, ) { AsyncActionView( @@ -60,7 +58,7 @@ fun SecureBackupEnterRecoveryKeyView( iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()), title = stringResource(id = R.string.screen_recovery_key_confirm_title), subTitle = stringResource(id = R.string.screen_recovery_key_confirm_description), - buttons = { Buttons(state = state, onCreateRecoveryKey = onCreateNewRecoveryKey) } + buttons = { Buttons(state = state) } ) { Content(state = state) } @@ -86,7 +84,6 @@ private fun Content( @Composable private fun ColumnScope.Buttons( state: SecureBackupEnterRecoveryKeyState, - onCreateRecoveryKey: () -> Unit, ) { Button( text = stringResource(id = CommonStrings.action_continue), @@ -97,12 +94,6 @@ private fun ColumnScope.Buttons( state.eventSink.invoke(SecureBackupEnterRecoveryKeyEvents.Submit) } ) - TextButton( - text = stringResource(id = R.string.screen_recovery_key_confirm_lost_recovery_key), - enabled = !state.submitAction.isLoading(), - modifier = Modifier.fillMaxWidth(), - onClick = onCreateRecoveryKey, - ) } @PreviewsDayNight @@ -114,6 +105,5 @@ internal fun SecureBackupEnterRecoveryKeyViewPreview( state = state, onSuccess = {}, onBackClick = {}, - onCreateNewRecoveryKey = {}, ) } 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 42b80441ce..784b2e3d00 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 @@ -101,18 +101,6 @@ class SecureBackupEnterRecoveryKeyViewTest { recorder.assertSingle(SecureBackupEnterRecoveryKeyEvents.Submit) } - @Test - @Config(qualifiers = "h1024dp") - fun `tapping on Lost your recovery key - calls onCreateNewRecoveryKey`() { - ensureCalledOnce { callback -> - rule.setSecureBackupEnterRecoveryKeyView( - aSecureBackupEnterRecoveryKeyState(), - onCreateNewRecoveryKey = callback, - ) - rule.clickOn(R.string.screen_recovery_key_confirm_lost_recovery_key) - } - } - @Test fun `when submit action succeeds - calls onDone`() { ensureCalledOnce { callback -> @@ -127,14 +115,12 @@ class SecureBackupEnterRecoveryKeyViewTest { state: SecureBackupEnterRecoveryKeyState, onDone: () -> Unit = EnsureNeverCalled(), onBackClick: () -> Unit = EnsureNeverCalled(), - onCreateNewRecoveryKey: () -> Unit = EnsureNeverCalled(), ) { setContent { SecureBackupEnterRecoveryKeyView( state = state, onSuccess = onDone, onBackClick = onBackClick, - onCreateNewRecoveryKey = onCreateNewRecoveryKey ) } } From dfaa3501673fb319e2d1f778a6302663a9651775 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Aug 2024 16:43:05 +0200 Subject: [PATCH 14/57] Better handling on null reset handler. This is not an error, but in this case, the reset identity is successful. --- .../impl/reset/ResetIdentityFlowManager.kt | 16 +++++++--------- .../impl/reset/ResetIdentityFlowNode.kt | 3 +++ .../impl/reset/ResetIdentityFlowManagerTest.kt | 4 +++- .../matrix/api/encryption/EncryptionService.kt | 2 +- .../impl/encryption/RustEncryptionService.kt | 7 ++++--- .../impl/encryption/RustIdentityResetHandle.kt | 16 ++++++++++------ 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt index 16850890a7..22e1bec945 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManager.kt @@ -36,8 +36,8 @@ class ResetIdentityFlowManager @Inject constructor( @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val sessionVerificationService: SessionVerificationService, ) { - private val resetHandleFlow: MutableStateFlow> = MutableStateFlow(AsyncData.Uninitialized) - val currentHandleFlow: StateFlow> = resetHandleFlow + private val resetHandleFlow: MutableStateFlow> = MutableStateFlow(AsyncData.Uninitialized) + val currentHandleFlow: StateFlow> = resetHandleFlow private var whenResetIsDoneWaitingJob: Job? = null fun whenResetIsDone(block: () -> Unit) { @@ -47,7 +47,7 @@ class ResetIdentityFlowManager @Inject constructor( } } - fun getResetHandle(): StateFlow> { + fun getResetHandle(): StateFlow> { return if (resetHandleFlow.value.isLoading() || resetHandleFlow.value.isSuccess()) { resetHandleFlow } else { @@ -56,13 +56,11 @@ class ResetIdentityFlowManager @Inject constructor( sessionCoroutineScope.launch { matrixClient.encryptionService().startIdentityReset() .onSuccess { handle -> - resetHandleFlow.value = if (handle != null) { - AsyncData.Success(handle) - } else { - AsyncData.Failure(IllegalStateException("Could not get a reset identity handle")) - } + resetHandleFlow.value = AsyncData.Success(handle) + } + .onFailure { + resetHandleFlow.value = AsyncData.Failure(it) } - .onFailure { resetHandleFlow.value = AsyncData.Failure(it) } } resetHandleFlow 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 9bd8aeff03..7900cf858d 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 @@ -140,6 +140,9 @@ class ResetIdentityFlowNode @AssistedInject constructor( } is AsyncData.Success -> { when (val handle = state.data) { + null -> { + Timber.d("No reset handle return, the reset is done.") + } is IdentityOidcResetHandle -> { if (oidcEntryPoint.canUseCustomTab()) { activity.openUrlInChromeCustomTab(null, false, handle.url) diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt index eb33fe5c36..2e03db7e36 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt @@ -73,7 +73,9 @@ class ResetIdentityFlowManagerTest { flowManager.getResetHandle().test { assertThat(awaitItem().isLoading()).isTrue() - assertThat(awaitItem().isFailure()).isTrue() + val finalItem = awaitItem() + assertThat(finalItem.isSuccess()).isTrue() + assertThat(finalItem.dataOrNull()).isNull() startResetLambda.assertions().isCalledOnce() } } 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 5afb81648b..ac5431a965 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 @@ -72,7 +72,7 @@ interface EncryptionService { /** * A handle to reset the user's identity. */ -interface IdentityResetHandle { +sealed interface IdentityResetHandle { /** * Cancel the reset process and drops the existing handle in the SDK. */ 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 ae681b2771..2040c772d6 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 @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.impl.encryption import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.extensions.flatMap import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.encryption.BackupState @@ -204,9 +205,9 @@ internal class RustEncryptionService( override suspend fun startIdentityReset(): Result { return runCatching { - service.resetIdentity()?.let { handle -> - RustIdentityResetHandleFactory.create(sessionId, handle) - }?.getOrNull() + service.resetIdentity() + }.flatMap { handle -> + RustIdentityResetHandleFactory.create(sessionId, handle) } } } 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 c4c20eb7d6..e6428d7fe8 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 @@ -27,13 +27,17 @@ import org.matrix.rustcomponents.sdk.CrossSigningResetAuthType object RustIdentityResetHandleFactory { fun create( userId: UserId, - identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle - ): Result { + identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle? + ): Result { return runCatching { - when (val authType = identityResetHandle.authType()) { - is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) - // User interactive authentication (user + password) - CrossSigningResetAuthType.Uiaa -> RustPasswordIdentityResetHandle(userId, identityResetHandle) + if (identityResetHandle == null) { + null + } else { + when (val authType = identityResetHandle.authType()) { + is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) + // User interactive authentication (user + password) + CrossSigningResetAuthType.Uiaa -> RustPasswordIdentityResetHandle(userId, identityResetHandle) + } } } } From 66a2f9fb489efde8182eddc8f1c0c1b86db77146 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 09:51:28 +0200 Subject: [PATCH 15/57] Sync English strings. --- features/securebackup/impl/src/main/res/values/localazy.xml | 2 +- features/verifysession/impl/src/main/res/values/localazy.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/securebackup/impl/src/main/res/values/localazy.xml b/features/securebackup/impl/src/main/res/values/localazy.xml index 6c428d4039..1a38c905bf 100644 --- a/features/securebackup/impl/src/main/res/values/localazy.xml +++ b/features/securebackup/impl/src/main/res/values/localazy.xml @@ -20,7 +20,7 @@ "You will lose your existing message history" "You will need to verify all your existing devices and contacts again" "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key." - "If you’re not signed in to any other devices and you’ve lost your recovery key, then you’ll need to reset your identity to continue using the app. " + "If you’re not signed in to any other devices and you’ve lost your recovery key, then you’ll need to reset your identity to continue using the app." "Reset your identity in case you can’t confirm another way" "Turn off" "You will lose your encrypted messages if you are signed out of all devices." diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index 45eda758a2..d8ce83bfe5 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -3,7 +3,7 @@ "Can\'t confirm?" "Create a new recovery key" "Verify this device to set up secure messaging." - "Confirm that it\'s you" + "Confirm your identity" "Use another device" "Use recovery key" "Now you can read or send messages securely, and anyone you chat with can also trust this device." From fd760823b19b3462c0917b32370c8ac65f2f9328 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 29 Aug 2024 08:08:12 +0000 Subject: [PATCH 16/57] Update screenshots --- ...ackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en.png | 3 --- ...kup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en.png | 3 --- ...p.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png | 4 ++-- ...p.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png | 4 ++-- ...p.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png | 4 ++-- ...p.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png | 4 ++-- ...impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png | 4 ++-- ...impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png | 4 ++-- ...impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png | 4 ++-- ...impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png | 4 ++-- ...ebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png | 4 ++-- ...ackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png | 4 ++-- ...ures.verifysession.impl_VerifySelfSessionView_Day_0_en.png | 4 ++-- ...ures.verifysession.impl_VerifySelfSessionView_Day_1_en.png | 4 ++-- ...ures.verifysession.impl_VerifySelfSessionView_Day_7_en.png | 4 ++-- ...ures.verifysession.impl_VerifySelfSessionView_Day_8_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_0_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_1_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_7_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_8_en.png | 4 ++-- 20 files changed, 36 insertions(+), 42 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en.png deleted file mode 100644 index 9c9bcd90e5..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d3d1bbc0c03ac483d0047bc4711eb63741c2071f013ee5962a1184e6112bef0c -size 60386 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en.png deleted file mode 100644 index 5205d00d66..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b2162a49d87b23c25251c6a8c322ec62eee2eb34e802ab5ce2ecacb637735554 -size 59189 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png index e17910c098..35512b032f 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:485585964998ee6bb7bb6ba208f2f465795a1b1ef59b3e44535b2d60ba9ef8a4 -size 37761 +oid sha256:ed7c737e5e7cfa37f588f17723be581ad24d217e8f5fe8ce52de85ac32753641 +size 33292 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png index 39ab9a25a3..c099492862 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95a05799a39675cfbb8c2df269131e5dd236c3298a1dadafe15b7c89a62be962 -size 49077 +oid sha256:48077b5e8a98b426335219721e8da6b378c702cb0b0b3902656b22df24a50b6d +size 44792 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png index a1d02d23dc..4530288bd3 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254973e1b41987fb6dd9db63b5acae4557467e2541867a4fb9448b88851e5fe4 -size 46993 +oid sha256:8217fdb69d5fba3e7bf2241d32a2821c72c9c628b0f97f77154ea53ff4a7bba7 +size 43206 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png index dfc236cd69..1f86bb2be3 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb58ee8ee32d1e308b898980f27317193bff4b0e00f800037834b02793b34dce -size 41111 +oid sha256:49f3902dbf6a146506166469ae727bc2925af6e19f1a9a6d65bf029467685f3d +size 37444 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png index 52e79a83d6..e893695af1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d2fc075202325aece0e74f962e5e6a387934d7782a1b39a9226c2b529ecf0b1 -size 36809 +oid sha256:2a3f34a36986bf00253ec1d85ec440d3b823853559863076784379cb08aa0060 +size 32393 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png index 8b5ec130aa..1639206339 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6909fcc10374f46d14ed628a4c1df0f4aba1fba874d2aa1b5554a74409dae861 -size 47737 +oid sha256:78a85eb229e35238976a5dffa8afa2647fee534a205dc1621ce60eb82d1e5a06 +size 43530 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png index ebd15db380..cbb9c12eda 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48e95b0eac9a83ce6abf5d82e904faf4081496d399eed9f8e4d806393f042a14 -size 46286 +oid sha256:28d59abaa2ac4185e3f2cd926557fd2a75cf7f60911d0af971fbbb34f8edd921 +size 42539 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png index 0e175854a2..8276038bda 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83c91d4e1bc3b9021c91d353b63a63c1893fdc79de57f91d8a6446b84bae657a -size 38441 +oid sha256:704f7d76ec2617a7cacecd5d6b3379f856c9e0d65cc8d328e9845435e3b5329c +size 34787 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png index 499713a15d..90ecf6e671 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9b7a1e2d20171a78da4ce4f590372e1d84f9624c4d56a011e74f91105c09f36 -size 79989 +oid sha256:45293f2b31c12ae11e2972e502618bad0a2cd78957ac0d80780ed43dff3b5727 +size 79919 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png index 8685aa5e42..002e8e86a7 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3c38d62929774fe6e6e003f656efbeec1f675b5dbb9994a917c4978f64d61a9 -size 78280 +oid sha256:be591f829e9deed65671eb9273ffa4319c0fcc6d7c155a83fdc4b8b0dee7048f +size 78322 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png index cf48a305b7..d3d92261ab 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee9c90a91ef8703c4878ea9314121ad8343dd3392c051e7e3e3079198a32ef99 -size 30555 +oid sha256:711074ce017e2909ae1cb610925bf7be96e182095cb08de5ca1d57b4dd206c92 +size 30834 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png index bfef3aada5..7a5e248136 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4b44b23029dd1ff76a082a29c0b39584a1e20c92795eb4815d632608abd2858 -size 22702 +oid sha256:4643c1011d807a1cb728b8cebab2c8c16276b680ccdd795a64fe449d1da80c7c +size 22982 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png index a2d07ed6a4..3910e7f8fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce48c81c355317abd6b9de4600e86492c3acde2807ca3572e064b61abc06239c -size 29426 +oid sha256:c242503ced2d16da2fd8c738e198689b60d00d40d423b52820aa01a622acd39a +size 29695 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png index 69eb60569f..c37fb53339 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d9810f3b326a05492c870ef22dbceec19112dba8beaa000066504cc96d1387a -size 23913 +oid sha256:d8d6fedd34ea54f9492bdc74f784aac902717328e3eda83f97d06e1eef970424 +size 24201 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png index 31ba47d4a4..d3d5c18a7a 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:453cab91aa056f3536c899354a2fb5de21519e7dce3d5675b1cbca702000e2c1 -size 29602 +oid sha256:df4be3e3577698e939dc02ebc0819768c04525a6b6cc67386e33501610dff5b2 +size 29959 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png index 89dcb3654d..436b9e460b 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f468a4a9140c4dc23ab3d394049f59b86f0ca2f332bee50abf8eaa0acce166b5 -size 22030 +oid sha256:76d12c58b8b1c60295a137136af5a6eed8d665600fb4f3e6feeaff714aa207ac +size 22396 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png index 39ce0ef07f..510ab80a6c 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c21120a08c871c7f64952c20be2c90b28590901f8e52893702775b6b2f176892 -size 28524 +oid sha256:a229523ad1f17f957adaffb47e8a64679bc3636ab94641bc492f98b7f5d3fe3d +size 28861 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png index 7d1bfb8eab..f66a5f6b92 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e46ffc3c3679eb5490d01c34d02314bae5611a7b10fb3c758331a28ae78f065 -size 23267 +oid sha256:b090b019f8d0a98b9e0ec4e4bd53628af15737eb441f36f40ba4abceb4c75608 +size 23622 From bc5ab0759ca41eac7d3daf1e6c8f954ffd458ffc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:07:43 +0200 Subject: [PATCH 17/57] Use `let`. --- .../matrix/impl/encryption/RustIdentityResetHandle.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 e6428d7fe8..0513046d0f 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 @@ -30,9 +30,7 @@ object RustIdentityResetHandleFactory { identityResetHandle: org.matrix.rustcomponents.sdk.IdentityResetHandle? ): Result { return runCatching { - if (identityResetHandle == null) { - null - } else { + identityResetHandle?.let { when (val authType = identityResetHandle.authType()) { is CrossSigningResetAuthType.Oidc -> RustOidcIdentityResetHandle(identityResetHandle, authType.info.approvalUrl) // User interactive authentication (user + password) From e4174b39e806ad356e35a080e40960f349823cfd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:13:57 +0200 Subject: [PATCH 18/57] Change test name. --- .../securebackup/impl/reset/ResetIdentityFlowManagerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt index 2e03db7e36..94c3139898 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowManagerTest.kt @@ -66,7 +66,7 @@ class ResetIdentityFlowManagerTest { } @Test - fun `getResetHandle - will fail if it receives a null reset handle`() = runTest { + fun `getResetHandle - will success if it receives a null reset handle`() = runTest { val startResetLambda = lambdaRecorder> { Result.success(null) } val encryptionService = FakeEncryptionService(startIdentityResetLambda = startResetLambda) val flowManager = createFlowManager(encryptionService = encryptionService) From 1026a296e33cf27b5dffa13b108469699e185027 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:47:22 +0200 Subject: [PATCH 19/57] Make LogoutUseCase.logout return the result of the SDK method. --- .../lockscreen/impl/unlock/PinUnlockPresenterTest.kt | 2 +- .../io/element/android/features/logout/api/LogoutUseCase.kt | 5 +++-- .../android/features/logout/impl/DefaultLogoutUseCase.kt | 6 +++--- .../android/features/logout/test/FakeLogoutUseCase.kt | 4 ++-- .../impl/developer/DeveloperSettingsPresenterTest.kt | 2 +- 5 files changed, 10 insertions(+), 9 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 8299b5a1ab..9aa43ee646 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 @@ -106,7 +106,7 @@ class PinUnlockPresenterTest { @Test fun `present - forgot pin flow`() = runTest { - val signOutLambda = lambdaRecorder { "" } + val signOutLambda = lambdaRecorder { "" } val signOut = FakeLogoutUseCase(signOutLambda) val presenter = createPinUnlockPresenter(this, logoutUseCase = signOut) moleculeFlow(RecompositionMode.Immediate) { diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt index a06b7117b1..692074dafb 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutUseCase.kt @@ -23,9 +23,10 @@ interface LogoutUseCase { /** * Log out the current user and then perform any needed cleanup tasks. * @param ignoreSdkError if true, the SDK error will be ignored and the user will be logged out anyway. - * @return the session id of the logged out user. + * @return an optional URL. When the URL is there, it should be presented to the user after logout for + * Relying Party (RP) initiated logout on their account page. */ - suspend fun logout(ignoreSdkError: Boolean): String + suspend fun logout(ignoreSdkError: Boolean): String? interface Factory { fun create(sessionId: String): LogoutUseCase diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt index 8e5f08a87a..ffd8d11406 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt @@ -35,9 +35,9 @@ class DefaultLogoutUseCase @AssistedInject constructor( override fun create(sessionId: String): DefaultLogoutUseCase } - override suspend fun logout(ignoreSdkError: Boolean): String { + override suspend fun logout(ignoreSdkError: Boolean): String? { val matrixClient = matrixClientProvider.getOrRestore(SessionId(sessionId)).getOrThrow() - matrixClient.logout(ignoreSdkError = ignoreSdkError) - return sessionId + val result = matrixClient.logout(ignoreSdkError = ignoreSdkError) + return result } } diff --git a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt index bbc67765d1..452d466032 100644 --- a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt +++ b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutUseCase.kt @@ -20,9 +20,9 @@ import io.element.android.features.logout.api.LogoutUseCase import io.element.android.tests.testutils.lambda.lambdaError class FakeLogoutUseCase( - var logoutLambda: (Boolean) -> String = lambdaError() + var logoutLambda: (Boolean) -> String? = { lambdaError() } ) : LogoutUseCase { - override suspend fun logout(ignoreSdkError: Boolean): String { + override suspend fun logout(ignoreSdkError: Boolean): String? { return logoutLambda(ignoreSdkError) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 9fb1c8b37f..75c1a19f39 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -166,7 +166,7 @@ class DeveloperSettingsPresenterTest { @Test fun `present - toggling simplified sliding sync changes the preferences and logs out the user`() = runTest { - val logoutCallRecorder = lambdaRecorder { "" } + val logoutCallRecorder = lambdaRecorder { "" } val logoutUseCase = FakeLogoutUseCase(logoutLambda = logoutCallRecorder) val preferences = InMemoryAppPreferencesStore() val presenter = createDeveloperSettingsPresenter(preferencesStore = preferences, logoutUseCase = logoutUseCase) From a6e2a5d81f1c9db9682ad610b175dc37c70dab00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 11:49:57 +0200 Subject: [PATCH 20/57] Change PinUnlockState.signOutAction type to AsyncAction. --- .../features/lockscreen/impl/unlock/PinUnlockPresenter.kt | 5 +++-- .../features/lockscreen/impl/unlock/PinUnlockState.kt | 3 ++- .../lockscreen/impl/unlock/PinUnlockStateProvider.kt | 5 +++-- .../android/features/lockscreen/impl/unlock/PinUnlockView.kt | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) 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 b563b3ac70..c2f3b81873 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 @@ -30,6 +30,7 @@ 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.unlock.keypad.PinKeypadModel import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -61,7 +62,7 @@ class PinUnlockPresenter @Inject constructor( mutableStateOf(false) } val signOutAction = remember { - mutableStateOf>(AsyncData.Uninitialized) + mutableStateOf>(AsyncAction.Uninitialized) } var biometricUnlockResult by remember { mutableStateOf(null) @@ -177,7 +178,7 @@ class PinUnlockPresenter @Inject constructor( } } - private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { + private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { suspend { logoutUseCase.logout(ignoreSdkError = true) }.runCatchingUpdatingState(signOutAction) 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 cfe371eb3f..53a44609ef 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 @@ -19,6 +19,7 @@ package io.element.android.features.lockscreen.impl.unlock import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockError import io.element.android.features.lockscreen.impl.pin.model.PinEntry +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData data class PinUnlockState( @@ -26,7 +27,7 @@ data class PinUnlockState( val showWrongPinTitle: Boolean, val remainingAttempts: AsyncData, val showSignOutPrompt: Boolean, - val signOutAction: AsyncData, + val signOutAction: AsyncAction, val showBiometricUnlock: Boolean, val isUnlocked: Boolean, val biometricUnlockResult: BiometricUnlock.AuthenticationResult?, 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 151c3079ee..8cd55f141d 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 @@ -19,6 +19,7 @@ package io.element.android.features.lockscreen.impl.unlock import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.lockscreen.impl.biometric.BiometricUnlock import io.element.android.features.lockscreen.impl.pin.model.PinEntry +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData open class PinUnlockStateProvider : PreviewParameterProvider { @@ -30,7 +31,7 @@ open class PinUnlockStateProvider : PreviewParameterProvider { aPinUnlockState(showSignOutPrompt = true), aPinUnlockState(showBiometricUnlock = false), aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0), - aPinUnlockState(signOutAction = AsyncData.Loading()), + aPinUnlockState(signOutAction = AsyncAction.Loading), ) } @@ -42,7 +43,7 @@ fun aPinUnlockState( showBiometricUnlock: Boolean = true, biometricUnlockResult: BiometricUnlock.AuthenticationResult? = null, isUnlocked: Boolean = false, - signOutAction: AsyncData = AsyncData.Uninitialized, + signOutAction: AsyncAction = AsyncAction.Uninitialized, ) = PinUnlockState( pinEntry = AsyncData.Success(pinEntry), showWrongPinTitle = showWrongPinTitle, 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 70b6a6c634..89d313df9b 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 @@ -56,6 +56,7 @@ import io.element.android.features.lockscreen.impl.components.PinEntryTextField import io.element.android.features.lockscreen.impl.pin.model.PinDigit import io.element.android.features.lockscreen.impl.pin.model.PinEntry import io.element.android.features.lockscreen.impl.unlock.keypad.PinKeypad +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom import io.element.android.libraries.designsystem.components.ProgressDialog @@ -91,7 +92,7 @@ fun PinUnlockView( onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, ) } - if (state.signOutAction is AsyncData.Loading) { + if (state.signOutAction == AsyncAction.Loading) { ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) } if (state.showBiometricUnlockError) { From cfbd0c4c02951b2a552fcf583d52287d35b2ba22 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:16:38 +0200 Subject: [PATCH 21/57] Ensure success logout url is opened in all cases. --- .../lockscreen/impl/unlock/PinUnlockNode.kt | 5 +++ .../lockscreen/impl/unlock/PinUnlockView.kt | 21 +++++++++++-- .../impl/unlock/activity/PinUnlockActivity.kt | 7 ++++- features/logout/api/build.gradle.kts | 1 + .../android/features/logout/api/util/Util.kt | 31 +++++++++++++++++++ .../features/logout/impl/LogoutNode.kt | 10 +----- .../impl/root/PreferencesRootNode.kt | 9 +----- 7 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index f357869375..bb0c86d5a2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -16,9 +16,11 @@ package io.element.android.features.lockscreen.impl.unlock +import android.app.Activity import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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 @@ -26,6 +28,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -47,6 +50,7 @@ class PinUnlockNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val activity = LocalContext.current as Activity LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { onUnlock() @@ -57,6 +61,7 @@ class PinUnlockNode @AssistedInject constructor( // UnlockNode is only used for in-app unlock, so we can safely set isInAppUnlock to true. // It's set to false in PinUnlockActivity. isInAppUnlock = true, + onSuccessLogout = { onSuccessLogout(activity, it) }, modifier = modifier ) } 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 89d313df9b..494135e6c2 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 @@ -39,7 +39,9 @@ import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +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.focus.FocusRequester @@ -75,6 +77,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun PinUnlockView( state: PinUnlockState, isInAppUnlock: Boolean, + onSuccessLogout: (logoutUrlResult: String?) -> Unit, modifier: Modifier = Modifier, ) { OnLifecycleEvent { _, event -> @@ -92,9 +95,21 @@ fun PinUnlockView( onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, ) } - if (state.signOutAction == AsyncAction.Loading) { - ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + when (state.signOutAction) { + AsyncAction.Loading -> { + ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + } + is AsyncAction.Success -> { + val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) + LaunchedEffect(state) { + latestOnSuccessLogout(state.signOutAction.data) + } + } + AsyncAction.Confirming, + is AsyncAction.Failure, + AsyncAction.Uninitialized -> Unit } + if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", @@ -364,6 +379,7 @@ internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStateProvider: PinUnlockView( state = state, isInAppUnlock = true, + onSuccessLogout = {}, ) } } @@ -375,6 +391,7 @@ internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStateProvider::clas PinUnlockView( state = state, isInAppUnlock = false, + onSuccessLogout = {}, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index 7b7b16790f..cbb05909a1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -29,6 +29,7 @@ import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter import io.element.android.features.lockscreen.impl.unlock.PinUnlockView import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.architecture.bindings import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -53,7 +54,11 @@ class PinUnlockActivity : AppCompatActivity() { setContent { ElementThemeApp(appPreferencesStore) { val state = presenter.present() - PinUnlockView(state = state, isInAppUnlock = false) + PinUnlockView( + state = state, + isInAppUnlock = false, + onSuccessLogout = { onSuccessLogout(this, it) }, + ) } } lifecycleScope.launch { diff --git a/features/logout/api/build.gradle.kts b/features/logout/api/build.gradle.kts index 85532f5617..655ab29ee5 100644 --- a/features/logout/api/build.gradle.kts +++ b/features/logout/api/build.gradle.kts @@ -22,6 +22,7 @@ android { } dependencies { + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt new file mode 100644 index 0000000000..6b59c2d0ef --- /dev/null +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.logout.api.util + +import android.app.Activity +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import timber.log.Timber + +fun onSuccessLogout( + activity: Activity, + url: String?, +) { + Timber.d("Success logout with result url: $url") + url?.let { + activity.openUrlInChromeCustomTab(null, false, it) + } +} diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index 4f5bab10b9..c4f1fd9e71 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -28,9 +28,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint -import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope -import timber.log.Timber @ContributesNode(SessionScope::class) class LogoutNode @AssistedInject constructor( @@ -42,13 +41,6 @@ class LogoutNode @AssistedInject constructor( plugins().forEach { it.onChangeRecoveryKeyClick() } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 106f07ecc2..16642029e4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -30,10 +30,10 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutView +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser -import timber.log.Timber @ContributesNode(SessionScope::class) class PreferencesRootNode @AssistedInject constructor( @@ -94,13 +94,6 @@ class PreferencesRootNode @AssistedInject constructor( } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success (direct) logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - private fun onOpenNotificationSettings() { plugins().forEach { it.onOpenNotificationSettings() } } From 460e095e78b4e18232e23cbf8d414b1437f1380f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:23:39 +0200 Subject: [PATCH 22/57] Provide dark theme parameter. --- .../android/features/lockscreen/impl/unlock/PinUnlockNode.kt | 4 +++- .../lockscreen/impl/unlock/activity/PinUnlockActivity.kt | 4 +++- .../io/element/android/features/logout/api/util/Util.kt | 3 ++- .../io/element/android/features/logout/impl/LogoutNode.kt | 4 +++- .../features/preferences/impl/root/PreferencesRootNode.kt | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index bb0c86d5a2..e1f87a3c61 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -28,6 +28,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @@ -51,6 +52,7 @@ class PinUnlockNode @AssistedInject constructor( override fun View(modifier: Modifier) { val state = presenter.present() val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { onUnlock() @@ -61,7 +63,7 @@ class PinUnlockNode @AssistedInject constructor( // UnlockNode is only used for in-app unlock, so we can safely set isInAppUnlock to true. // It's set to false in PinUnlockActivity. isInAppUnlock = true, - onSuccessLogout = { onSuccessLogout(activity, it) }, + onSuccessLogout = { onSuccessLogout(activity, isDark, it) }, modifier = modifier ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index cbb05909a1..88e8daf9db 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -24,6 +24,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope +import io.element.android.compound.theme.ElementTheme import io.element.android.features.lockscreen.api.LockScreenLockState import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter @@ -54,10 +55,11 @@ class PinUnlockActivity : AppCompatActivity() { setContent { ElementThemeApp(appPreferencesStore) { val state = presenter.present() + val isDark = ElementTheme.isLightTheme.not() PinUnlockView( state = state, isInAppUnlock = false, - onSuccessLogout = { onSuccessLogout(this, it) }, + onSuccessLogout = { onSuccessLogout(this, isDark, it) }, ) } } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt index 6b59c2d0ef..81d224bdf1 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt @@ -22,10 +22,11 @@ import timber.log.Timber fun onSuccessLogout( activity: Activity, + darkTheme: Boolean, url: String?, ) { Timber.d("Success logout with result url: $url") url?.let { - activity.openUrlInChromeCustomTab(null, false, it) + activity.openUrlInChromeCustomTab(null, darkTheme, it) } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index c4f1fd9e71..8c6948b227 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -27,6 +27,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.LogoutEntryPoint import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @@ -45,10 +46,11 @@ class LogoutNode @AssistedInject constructor( override fun View(modifier: Modifier) { val state = presenter.present() val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() LogoutView( state = state, onChangeRecoveryKeyClick = ::onChangeRecoveryKeyClick, - onSuccessLogout = { onSuccessLogout(activity, it) }, + onSuccessLogout = { onSuccessLogout(activity, isDark, it) }, onBackClick = ::navigateUp, modifier = modifier, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 16642029e4..b05031661b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -146,7 +146,7 @@ class PreferencesRootNode @AssistedInject constructor( directLogoutView.Render( state = state.directLogoutState, onSuccessLogout = { - onSuccessLogout(activity, it) + onSuccessLogout(activity, isDark, it) } ) } From aae44875bdb0adb4041394d536a8f0fd5b2c2e7c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:38:58 +0200 Subject: [PATCH 23/57] Fix test. --- .../lockscreen/impl/unlock/PinUnlockPresenterTest.kt | 5 +++-- 1 file changed, 3 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 9aa43ee646..3bb8998663 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 @@ -29,6 +29,7 @@ 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.unlock.keypad.PinKeypadModel import io.element.android.features.logout.test.FakeLogoutUseCase +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 @@ -51,7 +52,7 @@ class PinUnlockPresenterTest { assertThat(state.showWrongPinTitle).isFalse() assertThat(state.showSignOutPrompt).isFalse() assertThat(state.isUnlocked).isFalse() - assertThat(state.signOutAction).isInstanceOf(AsyncData.Uninitialized::class.java) + assertThat(state.signOutAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(state.remainingAttempts).isInstanceOf(AsyncData.Uninitialized::class.java) } awaitItem().also { state -> @@ -133,7 +134,7 @@ class PinUnlockPresenterTest { } skipItems(2) awaitItem().also { state -> - assertThat(state.signOutAction).isInstanceOf(AsyncData.Success::class.java) + assertThat(state.signOutAction).isInstanceOf(AsyncAction.Success::class.java) } assert(signOutLambda).isCalledOnce() } From 1a8e1a3e584dbc6ba92a53d1d9ba8592795a409a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:52:51 +0200 Subject: [PATCH 24/57] Fix Maestro test. --- .../tests/assertions/assertSessionVerificationDisplayed.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.maestro/tests/assertions/assertSessionVerificationDisplayed.yaml b/.maestro/tests/assertions/assertSessionVerificationDisplayed.yaml index 6690dfddb4..f983ced873 100644 --- a/.maestro/tests/assertions/assertSessionVerificationDisplayed.yaml +++ b/.maestro/tests/assertions/assertSessionVerificationDisplayed.yaml @@ -1,5 +1,5 @@ appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: - visible: "Confirm that it's you" + visible: "Confirm your identity" timeout: 20000 From 8b5fcf611f8e59d3a2265704307f90a5de8fe49c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 12:47:38 +0000 Subject: [PATCH 25/57] Update dependency com.google.testparameterinjector:test-parameter-injector to v1.17 --- 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 d6b552feff..50b66b7a56 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -143,7 +143,7 @@ test_mockk = "io.mockk:mockk:1.13.12" test_konsist = "com.lemonappdev:konsist:0.15.1" test_turbine = "app.cash.turbine:turbine:1.1.0" test_truth = "com.google.truth:truth:1.4.4" -test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.16" +test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.17" test_robolectric = "org.robolectric:robolectric:4.13" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } test_composable_preview_scanner = "com.github.sergio-sastre.ComposablePreviewScanner:android:0.1.2" From f5a685e3023e1c5c4ef8a6ab0f97d130917afaba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 12:47:43 +0000 Subject: [PATCH 26/57] Update dependency com.posthog:posthog-android to v3.6.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 d6b552feff..68aab45986 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -187,7 +187,7 @@ zxing_cpp = "io.github.zxing-cpp:android:2.2.0" play_services_oss_licenses = "com.google.android.gms:play-services-oss-licenses:17.1.0" # Analytics -posthog = "com.posthog:posthog-android:3.5.1" +posthog = "com.posthog:posthog-android:3.6.0" sentry = "io.sentry:sentry-android:7.14.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.1" From f4beddef9932edbb844c10cc3ab116d459bdb0a5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 15:56:08 +0200 Subject: [PATCH 27/57] Add a way to sign out when the user is asked to verify the session. --- features/verifysession/impl/build.gradle.kts | 2 + .../impl/VerifySelfSessionNode.kt | 7 +++ .../impl/VerifySelfSessionPresenter.kt | 17 +++++++ .../impl/VerifySelfSessionState.kt | 2 + .../impl/VerifySelfSessionStateProvider.kt | 7 +++ .../impl/VerifySelfSessionView.kt | 49 ++++++++++++++----- .../impl/VerifySelfSessionViewEvents.kt | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../impl/VerifySelfSessionPresenterTest.kt | 31 ++++++++++++ .../impl/VerifySelfSessionViewTest.kt | 18 +++++++ tools/localazy/config.json | 1 + 11 files changed, 125 insertions(+), 11 deletions(-) diff --git a/features/verifysession/impl/build.gradle.kts b/features/verifysession/impl/build.gradle.kts index 8c1028dabd..2cd2c7b7e3 100644 --- a/features/verifysession/impl/build.gradle.kts +++ b/features/verifysession/impl/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.preferences.api) implementation(projects.libraries.uiStrings) + implementation(projects.features.logout.api) api(libs.statemachine) api(projects.features.verifysession.api) @@ -52,6 +53,7 @@ dependencies { testImplementation(libs.test.robolectric) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(projects.features.logout.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.tests.testutils) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt index 0ed9524626..72640d3dbc 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt @@ -16,8 +16,10 @@ package io.element.android.features.verifysession.impl +import android.app.Activity import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -25,6 +27,8 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.libraries.di.SessionScope @@ -39,12 +43,15 @@ class VerifySelfSessionNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val activity = LocalContext.current as Activity + val isDark = ElementTheme.isLightTheme.not() VerifySelfSessionView( state = state, modifier = modifier, onEnterRecoveryKey = callback::onEnterRecoveryKey, onResetKey = callback::onResetKey, onFinish = callback::onDone, + onSuccessLogout = { onSuccessLogout(activity, isDark, it) }, ) } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt index ab06dd4915..03da02e4b3 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenter.kt @@ -20,14 +20,19 @@ package io.element.android.features.verifysession.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState 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 androidx.compose.runtime.rememberCoroutineScope import com.freeletics.flowredux.compose.rememberStateAndDispatch +import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState @@ -49,6 +54,7 @@ class VerifySelfSessionPresenter @Inject constructor( private val stateMachine: VerifySelfSessionStateMachine, private val buildMeta: BuildMeta, private val sessionPreferencesStore: SessionPreferencesStore, + private val logoutUseCase: LogoutUseCase, ) : Presenter { @Composable override fun present(): VerifySelfSessionState { @@ -61,6 +67,9 @@ class VerifySelfSessionPresenter @Inject constructor( val stateAndDispatch = stateMachine.rememberStateAndDispatch() val skipVerification by sessionPreferencesStore.isSessionVerificationSkipped().collectAsState(initial = false) val needsVerification by sessionVerificationService.needsSessionVerification.collectAsState(initial = true) + val signOutAction = remember { + mutableStateOf>(AsyncAction.Uninitialized) + } val verificationFlowStep by remember { derivedStateOf { when { @@ -85,6 +94,7 @@ class VerifySelfSessionPresenter @Inject constructor( VerifySelfSessionViewEvents.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) VerifySelfSessionViewEvents.Cancel -> stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) VerifySelfSessionViewEvents.Reset -> stateAndDispatch.dispatchAction(StateMachineEvent.Reset) + VerifySelfSessionViewEvents.SignOut -> coroutineScope.signOut(signOutAction) VerifySelfSessionViewEvents.SkipVerification -> coroutineScope.launch { sessionPreferencesStore.setSkipSessionVerification(true) } @@ -92,6 +102,7 @@ class VerifySelfSessionPresenter @Inject constructor( } return VerifySelfSessionState( verificationFlowStep = verificationFlowStep, + signOutAction = signOutAction.value, displaySkipButton = buildMeta.isDebuggable, eventSink = ::handleEvents, ) @@ -160,4 +171,10 @@ class VerifySelfSessionPresenter @Inject constructor( } }.launchIn(this) } + + private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { + suspend { + logoutUseCase.logout(ignoreSdkError = true) + }.runCatchingUpdatingState(signOutAction) + } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt index 4db21d88b8..aca96eea1c 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionState.kt @@ -18,12 +18,14 @@ package io.element.android.features.verifysession.impl import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.verification.SessionVerificationData @Immutable data class VerifySelfSessionState( val verificationFlowStep: VerificationStep, + val signOutAction: AsyncAction, val displaySkipButton: Boolean, val eventSink: (VerifySelfSessionViewEvents) -> Unit, ) { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt index f6db1bc65f..379d9b1686 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionStateProvider.kt @@ -18,6 +18,7 @@ package io.element.android.features.verifysession.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.VerificationEmoji @@ -54,6 +55,10 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider = AsyncAction.Uninitialized, displaySkipButton: Boolean = false, eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, ) = VerifySelfSessionState( verificationFlowStep = verificationFlowStep, displaySkipButton = displaySkipButton, eventSink = eventSink, + signOutAction = signOutAction, ) private fun aVerificationEmojiList() = listOf( diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 446114752e..c3b044a5a1 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -46,11 +47,13 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.verifysession.impl.emoji.toEmojiResource +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.PageTitle +import io.element.android.libraries.designsystem.components.ProgressDialog 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 @@ -70,11 +73,13 @@ fun VerifySelfSessionView( onEnterRecoveryKey: () -> Unit, onResetKey: () -> Unit, onFinish: () -> Unit, + onSuccessLogout: (String?) -> Unit, modifier: Modifier = Modifier, ) { fun resetFlow() { state.eventSink(VerifySelfSessionViewEvents.Reset) } + val latestOnFinish by rememberUpdatedState(newValue = onFinish) LaunchedEffect(state.verificationFlowStep, latestOnFinish) { if (state.verificationFlowStep is FlowStep.Skipped) { @@ -97,17 +102,23 @@ fun VerifySelfSessionView( HeaderFooterPage( modifier = modifier, topBar = { - TopAppBar( - title = {}, - actions = { - if (state.displaySkipButton && state.verificationFlowStep != FlowStep.Completed) { - TextButton( - text = stringResource(CommonStrings.action_skip), - onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } - ) - } - } - ) + TopAppBar( + title = {}, + actions = { + if (state.verificationFlowStep != FlowStep.Completed) { + if (state.displaySkipButton && LocalInspectionMode.current.not()) { + TextButton( + text = stringResource(CommonStrings.action_skip), + onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } + ) + } + TextButton( + text = stringResource(CommonStrings.action_signout), + onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) } + ) + } + } + ) }, header = { HeaderContent(verificationFlowStep = verificationFlowStep) @@ -124,6 +135,21 @@ fun VerifySelfSessionView( ) { Content(flowState = verificationFlowStep) } + + when (state.signOutAction) { + AsyncAction.Loading -> { + ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + } + is AsyncAction.Success -> { + val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) + LaunchedEffect(state) { + latestOnSuccessLogout(state.signOutAction.data) + } + } + AsyncAction.Confirming, + is AsyncAction.Failure, + AsyncAction.Uninitialized -> Unit + } } @Composable @@ -367,5 +393,6 @@ internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionSta onEnterRecoveryKey = {}, onResetKey = {}, onFinish = {}, + onSuccessLogout = {}, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt index 2c6b776f7b..59c47e4641 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewEvents.kt @@ -23,5 +23,6 @@ sealed interface VerifySelfSessionViewEvents { data object DeclineVerification : VerifySelfSessionViewEvents data object Cancel : VerifySelfSessionViewEvents data object Reset : VerifySelfSessionViewEvents + data object SignOut : VerifySelfSessionViewEvents data object SkipVerification : VerifySelfSessionViewEvents } diff --git a/features/verifysession/impl/src/main/res/values/localazy.xml b/features/verifysession/impl/src/main/res/values/localazy.xml index d8ce83bfe5..4b5f49a875 100644 --- a/features/verifysession/impl/src/main/res/values/localazy.xml +++ b/features/verifysession/impl/src/main/res/values/localazy.xml @@ -28,4 +28,5 @@ "They match" "Accept the request to start the verification process in your other session to continue." "Waiting to accept request" + "Signing out…" diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt index 6fd13c45bf..c258b9df03 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTest.kt @@ -21,6 +21,8 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.logout.api.LogoutUseCase +import io.element.android.features.logout.test.FakeLogoutUseCase import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.meta.BuildMeta @@ -36,6 +38,8 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -309,6 +313,31 @@ class VerifySelfSessionPresenterTest { } } + @Test + fun `present - When user request to sign out, the sign out use case is invoked`() = runTest { + val service = FakeSessionVerificationService().apply { + givenNeedsSessionVerification(false) + givenVerifiedStatus(SessionVerifiedStatus.Verified) + givenVerificationFlowState(VerificationFlowState.Finished) + } + val signOutLambda = lambdaRecorder { "aUrl" } + val presenter = createVerifySelfSessionPresenter( + service, + logoutUseCase = FakeLogoutUseCase(signOutLambda) + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialItem = awaitItem() + initialItem.eventSink(VerifySelfSessionViewEvents.SignOut) + val finalItem = awaitItem() + assertThat(finalItem.signOutAction.isSuccess()).isTrue() + assertThat(finalItem.signOutAction.dataOrNull()).isEqualTo("aUrl") + signOutLambda.assertions().isCalledOnce().with(value(true)) + } + } + private suspend fun ReceiveTurbine.requestVerificationAndAwaitVerifyingState( fakeService: FakeSessionVerificationService, sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), @@ -344,6 +373,7 @@ class VerifySelfSessionPresenterTest { encryptionService: EncryptionService = FakeEncryptionService(), buildMeta: BuildMeta = aBuildMeta(), sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), + logoutUseCase: LogoutUseCase = FakeLogoutUseCase(), ): VerifySelfSessionPresenter { return VerifySelfSessionPresenter( sessionVerificationService = service, @@ -351,6 +381,7 @@ class VerifySelfSessionPresenterTest { stateMachine = VerifySelfSessionStateMachine(service, encryptionService), buildMeta = buildMeta, sessionPreferencesStore = sessionPreferencesStore, + logoutUseCase = logoutUseCase, ) } } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt index 7e5bf928e2..919dba1efd 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionViewTest.kt @@ -20,6 +20,7 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled @@ -27,6 +28,7 @@ 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.ensureCalledOnce +import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBackKey import org.junit.Rule import org.junit.Test @@ -213,11 +215,26 @@ class VerifySelfSessionViewTest { } } + @Test + fun `on success logout - onFinished callback is called immediately`() { + val aUrl = "aUrl" + ensureCalledOnceWithParam(aUrl) { callback -> + rule.setVerifySelfSessionView( + aVerifySelfSessionState( + signOutAction = AsyncAction.Success(aUrl), + eventSink = EnsureNeverCalledWithParam(), + ), + onSuccessLogout = callback, + ) + } + } + private fun AndroidComposeTestRule.setVerifySelfSessionView( state: VerifySelfSessionState, onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(), onResetKey: () -> Unit = EnsureNeverCalled(), + onSuccessLogout: (String?) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { VerifySelfSessionView( @@ -225,6 +242,7 @@ class VerifySelfSessionViewTest { onEnterRecoveryKey = onEnterRecoveryKey, onFinish = onFinished, onResetKey = onResetKey, + onSuccessLogout = onSuccessLogout, ) } } diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 82fb4f2277..c7b9192a23 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -55,6 +55,7 @@ "name" : ":features:verifysession:impl", "includeRegex" : [ "screen_session_verification_.*", + "screen_signout_in_progress_dialog_content", "screen_identity_.*" ] }, From 9134d334bb13cdb2d1a6bb1aa7394321858bfcce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 16:29:30 +0200 Subject: [PATCH 28/57] Show sign out button only at initial step. --- .../verifysession/impl/VerifySelfSessionView.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index c3b044a5a1..7987272489 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -105,13 +105,15 @@ fun VerifySelfSessionView( TopAppBar( title = {}, actions = { - if (state.verificationFlowStep != FlowStep.Completed) { - if (state.displaySkipButton && LocalInspectionMode.current.not()) { - TextButton( - text = stringResource(CommonStrings.action_skip), - onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } - ) - } + if (state.verificationFlowStep != FlowStep.Completed && + state.displaySkipButton && + LocalInspectionMode.current.not()) { + TextButton( + text = stringResource(CommonStrings.action_skip), + onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } + ) + } + if (state.verificationFlowStep is FlowStep.Initial) { TextButton( text = stringResource(CommonStrings.action_signout), onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) } From cf31895e9c49119c8949f01dfba0793408acaba9 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 29 Aug 2024 14:39:56 +0000 Subject: [PATCH 29/57] Update screenshots --- ...ures.verifysession.impl_VerifySelfSessionView_Day_0_en.png | 4 ++-- ...res.verifysession.impl_VerifySelfSessionView_Day_10_en.png | 3 +++ ...ures.verifysession.impl_VerifySelfSessionView_Day_7_en.png | 4 ++-- ...ures.verifysession.impl_VerifySelfSessionView_Day_8_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_0_en.png | 4 ++-- ...s.verifysession.impl_VerifySelfSessionView_Night_10_en.png | 3 +++ ...es.verifysession.impl_VerifySelfSessionView_Night_7_en.png | 4 ++-- ...es.verifysession.impl_VerifySelfSessionView_Night_8_en.png | 4 ++-- 8 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png index d3d92261ab..b7f2cb61fb 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:711074ce017e2909ae1cb610925bf7be96e182095cb08de5ca1d57b4dd206c92 -size 30834 +oid sha256:205fc4655fed7b11ef133e0df13b8175676ff38d398fde45a2e6a3b140a216e2 +size 31440 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png new file mode 100644 index 0000000000..0f5d4e1362 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f7797415de4a52095a47dc7beb5b51c7528a90e9c8daf5e4d3eff030b22f3f7 +size 32596 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png index 3910e7f8fa..b7f2cb61fb 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c242503ced2d16da2fd8c738e198689b60d00d40d423b52820aa01a622acd39a -size 29695 +oid sha256:205fc4655fed7b11ef133e0df13b8175676ff38d398fde45a2e6a3b140a216e2 +size 31440 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png index c37fb53339..95204c04f9 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8d6fedd34ea54f9492bdc74f784aac902717328e3eda83f97d06e1eef970424 -size 24201 +oid sha256:2a8350fe244b42863ee1fb60c403aa2c695a5152755f73bf27598e71e033ef0b +size 25962 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png index d3d5c18a7a..2d4c2d9f62 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df4be3e3577698e939dc02ebc0819768c04525a6b6cc67386e33501610dff5b2 -size 29959 +oid sha256:4307ad367eb24d8f7590dfadf4e4b728ec4845ba4720b72824f5b2c60175f8fd +size 30555 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png new file mode 100644 index 0000000000..a06ee6d018 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a66446c1dc2ff1e32fd06ee722aaaed578369e6de0a0024d435104bcadc47d49 +size 31012 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png index 510ab80a6c..2d4c2d9f62 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a229523ad1f17f957adaffb47e8a64679bc3636ab94641bc492f98b7f5d3fe3d -size 28861 +oid sha256:4307ad367eb24d8f7590dfadf4e4b728ec4845ba4720b72824f5b2c60175f8fd +size 30555 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png index f66a5f6b92..b023c7b6d6 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl_VerifySelfSessionView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b090b019f8d0a98b9e0ec4e4bd53628af15737eb441f36f40ba4abceb4c75608 -size 23622 +oid sha256:b0da42a76838a54311a5ea6ec54725f3bb9c91c4ff7bc09ff571d6cd7b6796e1 +size 25369 From ad8e8305eb5a9979bb70f4022f6fcfffa3e72512 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 17:24:22 +0200 Subject: [PATCH 30/57] Add banner entry point to set up recovery --- .../android/appnav/LoggedInFlowNode.kt | 4 ++ .../roomlist/api/RoomListEntryPoint.kt | 1 + .../features/roomlist/impl/RoomListNode.kt | 5 ++ .../roomlist/impl/RoomListPresenter.kt | 11 ++++- .../features/roomlist/impl/RoomListState.kt | 1 + .../roomlist/impl/RoomListStateProvider.kt | 1 + .../features/roomlist/impl/RoomListView.kt | 5 ++ .../impl/components/RoomListContentView.kt | 38 +++++++++----- .../impl/components/SetUpRecoveryKeyBanner.kt | 49 +++++++++++++++++++ .../impl/src/main/res/values/localazy.xml | 2 + .../roomlist/impl/RoomListPresenterTest.kt | 2 +- .../roomlist/impl/RoomListViewTest.kt | 41 ++++++++++++++++ .../api/SecureBackupEntryPoint.kt | 3 ++ .../securebackup/impl/SecureBackupFlowNode.kt | 1 + .../android/samples/minimal/RoomListScreen.kt | 1 + tools/localazy/config.json | 1 + 16 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.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 cc50e19895..a7101e4f21 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -259,6 +259,10 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.CreateRoom) } + override fun onSetUpRecoveryClick() { + backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.SetUpRecovery)) + } + override fun onSessionConfirmRecoveryKeyClick() { backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey)) } diff --git a/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt b/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt index 86d3e7cd1a..6242c6ec3e 100644 --- a/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt +++ b/features/roomlist/api/src/main/kotlin/io/element/android/features/roomlist/api/RoomListEntryPoint.kt @@ -33,6 +33,7 @@ interface RoomListEntryPoint : FeatureEntryPoint { fun onRoomClick(roomId: RoomId) fun onCreateRoomClick() fun onSettingsClick() + fun onSetUpRecoveryClick() fun onSessionConfirmRecoveryKeyClick() fun onRoomSettingsClick(roomId: RoomId) fun onReportBugClick() diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt index d77fd1f440..7e6bc019e9 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListNode.kt @@ -66,6 +66,10 @@ class RoomListNode @AssistedInject constructor( plugins().forEach { it.onCreateRoomClick() } } + private fun onSetUpRecoveryClick() { + plugins().forEach { it.onSetUpRecoveryClick() } + } + private fun onSessionConfirmRecoveryKeyClick() { plugins().forEach { it.onSessionConfirmRecoveryKeyClick() } } @@ -98,6 +102,7 @@ class RoomListNode @AssistedInject constructor( onRoomClick = this::onRoomClick, onSettingsClick = this::onOpenSettings, onCreateRoomClick = this::onCreateRoomClick, + onSetUpRecoveryClick = this::onSetUpRecoveryClick, onConfirmRecoveryKeyClick = this::onSessionConfirmRecoveryKeyClick, onRoomSettingsClick = this::onRoomSettingsClick, onMenuActionClick = { onMenuActionClick(activity, it) }, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index d5f9404eff..20fcb1fc0b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -187,8 +187,15 @@ class RoomListPresenter @Inject constructor( derivedStateOf { when { currentSecurityBannerDismissed -> SecurityBannerState.None - recoveryState == RecoveryState.INCOMPLETE && - syncState == SyncState.Running -> SecurityBannerState.RecoveryKeyConfirmation + syncState == SyncState.Running -> { + when (recoveryState) { + RecoveryState.UNKNOWN, + RecoveryState.DISABLED -> SecurityBannerState.SetUpRecovery + RecoveryState.INCOMPLETE -> SecurityBannerState.RecoveryKeyConfirmation + RecoveryState.WAITING_FOR_SYNC, + RecoveryState.ENABLED -> SecurityBannerState.None + } + } else -> SecurityBannerState.None } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt index f29c9d24d4..fe824aa158 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListState.kt @@ -66,6 +66,7 @@ enum class InvitesState { enum class SecurityBannerState { None, + SetUpRecovery, RecoveryKeyConfirmation, } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt index fe000c948e..8e1d0ac450 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListStateProvider.kt @@ -52,6 +52,7 @@ open class RoomListStateProvider : PreviewParameterProvider { aRoomListState(contentState = aSkeletonContentState()), aRoomListState(matrixUser = MatrixUser(userId = UserId("@id:domain")), contentState = aMigrationContentState()), aRoomListState(searchState = aRoomListSearchState(isSearchActive = true, query = "Test")), + aRoomListState(contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SetUpRecovery)), ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index 03fdfbdc3b..eba59e8124 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -53,6 +53,7 @@ fun RoomListView( state: RoomListState, onRoomClick: (RoomId) -> Unit, onSettingsClick: () -> Unit, + onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onCreateRoomClick: () -> Unit, onRoomSettingsClick: (roomId: RoomId) -> Unit, @@ -78,6 +79,7 @@ fun RoomListView( RoomListScaffold( state = state, + onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onRoomClick = onRoomClick, onOpenSettings = onSettingsClick, @@ -106,6 +108,7 @@ fun RoomListView( @Composable private fun RoomListScaffold( state: RoomListState, + onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onRoomClick: (RoomId) -> Unit, onOpenSettings: () -> Unit, @@ -142,6 +145,7 @@ private fun RoomListScaffold( contentState = state.contentState, filtersState = state.filtersState, eventSink = state.eventSink, + onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onRoomClick = ::onRoomClick, onCreateRoomClick = onCreateRoomClick, @@ -178,6 +182,7 @@ internal fun RoomListViewPreview(@PreviewParameter(RoomListStateProvider::class) state = state, onRoomClick = {}, onSettingsClick = {}, + onSetUpRecoveryClick = {}, onConfirmRecoveryKeyClick = {}, onCreateRoomClick = {}, onRoomSettingsClick = {}, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt index 5bc85d3458..e60c132490 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListContentView.kt @@ -70,6 +70,7 @@ fun RoomListContentView( contentState: RoomListContentState, filtersState: RoomListFiltersState, eventSink: (RoomListEvents) -> Unit, + onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onRoomClick: (RoomListRoomSummary) -> Unit, onCreateRoomClick: () -> Unit, @@ -95,6 +96,7 @@ fun RoomListContentView( state = contentState, filtersState = filtersState, eventSink = eventSink, + onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onRoomClick = onRoomClick, ) @@ -141,6 +143,7 @@ private fun RoomsView( state: RoomListContentState.Rooms, filtersState: RoomListFiltersState, eventSink: (RoomListEvents) -> Unit, + onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onRoomClick: (RoomListRoomSummary) -> Unit, modifier: Modifier = Modifier, @@ -154,6 +157,7 @@ private fun RoomsView( RoomsViewList( state = state, eventSink = eventSink, + onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onRoomClick = onRoomClick, modifier = modifier.fillMaxSize(), @@ -165,6 +169,7 @@ private fun RoomsView( private fun RoomsViewList( state: RoomListContentState.Rooms, eventSink: (RoomListEvents) -> Unit, + onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onRoomClick: (RoomListRoomSummary) -> Unit, modifier: Modifier = Modifier, @@ -188,21 +193,27 @@ private fun RoomsViewList( // FAB height is 56dp, bottom padding is 16dp, we add 8dp as extra margin -> 56+16+8 = 80 contentPadding = PaddingValues(bottom = 80.dp) ) { - if (state.securityBannerState != SecurityBannerState.None) { - when (state.securityBannerState) { - SecurityBannerState.RecoveryKeyConfirmation -> { - item { - ConfirmRecoveryKeyBanner( - onContinueClick = onConfirmRecoveryKeyClick, - onDismissClick = { updatedEventSink(RoomListEvents.DismissRecoveryKeyPrompt) } - ) - } + when (state.securityBannerState) { + SecurityBannerState.SetUpRecovery -> { + item { + SetUpRecoveryKeyBanner( + onContinueClick = onSetUpRecoveryClick, + onDismissClick = { updatedEventSink(RoomListEvents.DismissRecoveryKeyPrompt) } + ) } - else -> Unit } - } else if (state.fullScreenIntentPermissionsState.shouldDisplayBanner) { - item { - FullScreenIntentPermissionBanner(state = state.fullScreenIntentPermissionsState) + SecurityBannerState.RecoveryKeyConfirmation -> { + item { + ConfirmRecoveryKeyBanner( + onContinueClick = onConfirmRecoveryKeyClick, + onDismissClick = { updatedEventSink(RoomListEvents.DismissRecoveryKeyPrompt) } + ) + } + } + SecurityBannerState.None -> if (state.fullScreenIntentPermissionsState.shouldDisplayBanner) { + item { + FullScreenIntentPermissionBanner(state = state.fullScreenIntentPermissionsState) + } } } @@ -276,6 +287,7 @@ internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePr filterSelectionStates = RoomListFilter.entries.map { FilterSelectionState(it, isSelected = true) } ), eventSink = {}, + onSetUpRecoveryClick = {}, onConfirmRecoveryKeyClick = {}, onRoomClick = {}, onCreateRoomClick = {}, diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt new file mode 100644 index 0000000000..56fd1b5312 --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/SetUpRecoveryKeyBanner.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomlist.impl.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import io.element.android.features.roomlist.impl.R +import io.element.android.libraries.designsystem.atomic.molecules.DialogLikeBannerMolecule +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight + +@Composable +internal fun SetUpRecoveryKeyBanner( + onContinueClick: () -> Unit, + onDismissClick: () -> Unit, + modifier: Modifier = Modifier, +) { + DialogLikeBannerMolecule( + modifier = modifier, + title = stringResource(R.string.banner_set_up_recovery_title), + content = stringResource(R.string.banner_set_up_recovery_content), + onSubmitClick = onContinueClick, + onDismissClick = onDismissClick, + ) +} + +@PreviewsDayNight +@Composable +internal fun SetUpRecoveryKeyBannerPreview() = ElementPreview { + SetUpRecoveryKeyBanner( + onContinueClick = {}, + onDismissClick = {}, + ) +} diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index f8aff41e8f..6affb2bb16 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -1,5 +1,7 @@ + "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices." + "Set up recovery" "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup." "Enter your recovery key" "To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked." diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index a381dc414d..bfa4e07656 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -264,7 +264,7 @@ class RoomListPresenterTest { val initialState = consumeItemsUntilPredicate { it.contentState is RoomListContentState.Rooms }.last() - assertThat(initialState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) + assertThat(initialState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.SetUpRecovery) encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE) val nextState = awaitItem() assertThat(nextState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt index 09cb6a8019..3e881fe294 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListViewTest.kt @@ -80,6 +80,24 @@ class RoomListViewTest { eventsRecorder.assertSingle(RoomListEvents.DismissRecoveryKeyPrompt) } + @Test + fun `clicking on close setup key banner emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setRoomListView( + state = aRoomListState( + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SetUpRecovery), + eventSink = eventsRecorder, + ) + ) + + // Remove automatic initial events + eventsRecorder.clear() + + val close = rule.activity.getString(CommonStrings.action_close) + rule.onNodeWithContentDescription(close).performClick() + eventsRecorder.assertSingle(RoomListEvents.DismissRecoveryKeyPrompt) + } + @Test fun `clicking on continue recovery key banner invokes the expected callback`() { val eventsRecorder = EventsRecorder() @@ -101,6 +119,27 @@ class RoomListViewTest { } } + @Test + fun `clicking on continue setup key banner invokes the expected callback`() { + val eventsRecorder = EventsRecorder() + ensureCalledOnce { callback -> + rule.setRoomListView( + state = aRoomListState( + contentState = aRoomsContentState(securityBannerState = SecurityBannerState.SetUpRecovery), + eventSink = eventsRecorder, + ), + onSetUpRecoveryClick = callback, + ) + + // Remove automatic initial events + eventsRecorder.clear() + + rule.clickOn(CommonStrings.action_continue) + + eventsRecorder.assertEmpty() + } + } + @Test fun `clicking on start chat when the session has no room invokes the expected callback`() { val eventsRecorder = EventsRecorder(expectEvents = false) @@ -208,6 +247,7 @@ private fun AndroidComposeTestRule.setRoomL state: RoomListState, onRoomClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onSettingsClick: () -> Unit = EnsureNeverCalled(), + onSetUpRecoveryClick: () -> Unit = EnsureNeverCalled(), onConfirmRecoveryKeyClick: () -> Unit = EnsureNeverCalled(), onCreateRoomClick: () -> Unit = EnsureNeverCalled(), onRoomSettingsClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(), @@ -219,6 +259,7 @@ private fun AndroidComposeTestRule.setRoomL state = state, onRoomClick = onRoomClick, onSettingsClick = onSettingsClick, + onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onCreateRoomClick = onCreateRoomClick, onRoomSettingsClick = onRoomSettingsClick, diff --git a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt index ab0d980a6e..bf1b0249a2 100644 --- a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt +++ b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt @@ -29,6 +29,9 @@ interface SecureBackupEntryPoint : FeatureEntryPoint { @Parcelize data object Root : InitialTarget + @Parcelize + data object SetUpRecovery : InitialTarget + @Parcelize data object EnterRecoveryKey : InitialTarget diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index 19a10aa47a..729c6c2b3e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -50,6 +50,7 @@ class SecureBackupFlowNode @AssistedInject constructor( backstack = BackStack( initialElement = when (plugins.filterIsInstance().first().initialElement) { SecureBackupEntryPoint.InitialTarget.Root -> NavTarget.Root + SecureBackupEntryPoint.InitialTarget.SetUpRecovery -> NavTarget.Setup SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey -> NavTarget.EnterRecoveryKey is SecureBackupEntryPoint.InitialTarget.ResetIdentity -> NavTarget.ResetIdentity }, diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index b91daeecfd..2a3dec60f3 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -178,6 +178,7 @@ class RoomListScreen( state = state, onRoomClick = ::onRoomClick, onSettingsClick = {}, + onSetUpRecoveryClick = {}, onConfirmRecoveryKeyClick = {}, onCreateRoomClick = {}, onRoomSettingsClick = {}, diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 82fb4f2277..dbd1ae7233 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -137,6 +137,7 @@ "screen_roomlist_.*", "session_verification_banner_.*", "confirm_recovery_key_banner_.*", + "banner\\.set_up_recovery\\..*", "full_screen_intent_banner_.*", "screen_migration_.*", "screen_invites_.*" From 2dc28561d4cafba9127c04fa12e7574f9eb520f3 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 29 Aug 2024 16:48:33 +0000 Subject: [PATCH 31/57] Update screenshots --- ...oomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png | 3 +++ ...mlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png | 3 +++ .../images/features.roomlist.impl_RoomListView_Day_11_en.png | 3 +++ .../images/features.roomlist.impl_RoomListView_Night_11_en.png | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_11_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_11_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png new file mode 100644 index 0000000000..7bc734b404 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb59f6e1771481bec3e96b89cbc9255685e6ea154412d7bf7e1351130f42173a +size 28896 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png new file mode 100644 index 0000000000..a2920c24ff --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:413e055704ff0c8dc82321b108c1fa90e7d58af1a781244fbde3daf45d638331 +size 27829 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_11_en.png new file mode 100644 index 0000000000..804a3351dd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Day_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5870fc3df4bb7330972727601016189e75c358f535f31fe6704837eb866866df +size 100363 diff --git a/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_11_en.png new file mode 100644 index 0000000000..2b92b26cc0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomlist.impl_RoomListView_Night_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d236927f6c4cc81f1e9c9b6d405e27e8e96ceb89f9047493b9ab590165d484b2 +size 104246 From d7256adeaadd898f7f7de5cc3b816b2cf143306a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:15:04 +0000 Subject: [PATCH 32/57] Update dependencyAnalysis to v2.0.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 50b66b7a56..fbacf9b223 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ test_core = "1.6.1" #other coil = "2.7.0" datetime = "0.6.0" -dependencyAnalysis = "2.0.0" +dependencyAnalysis = "2.0.1" serialization_json = "1.6.3" showkase = "1.0.3" appyx = "1.4.0" From 604f6476b53bebc598311c3e4a2d19d4eace0484 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:15:09 +0000 Subject: [PATCH 33/57] Update android.gradle.plugin to v8.6.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 50b66b7a56..ca7c011079 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ [versions] # Project -android_gradle_plugin = "8.5.2" +android_gradle_plugin = "8.6.0" kotlin = "1.9.25" ksp = "1.9.25-1.0.20" firebaseAppDistribution = "5.0.0" From d93762bbd04c683bc7b347d06f632664ead736f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 09:15:53 +0200 Subject: [PATCH 34/57] Increase test coverage of `securityBannerState()` --- .../features/roomlist/impl/RoomListPresenterTest.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt index bfa4e07656..762128d0cd 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTest.kt @@ -268,6 +268,17 @@ class RoomListPresenterTest { encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE) val nextState = awaitItem() assertThat(nextState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) + // Also check other states + encryptionService.emitRecoveryState(RecoveryState.DISABLED) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.SetUpRecovery) + encryptionService.emitRecoveryState(RecoveryState.WAITING_FOR_SYNC) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) + encryptionService.emitRecoveryState(RecoveryState.DISABLED) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.SetUpRecovery) + encryptionService.emitRecoveryState(RecoveryState.ENABLED) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) + encryptionService.emitRecoveryState(RecoveryState.DISABLED) + assertThat(awaitItem().contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.SetUpRecovery) nextState.eventSink(RoomListEvents.DismissRecoveryKeyPrompt) val finalState = awaitItem() assertThat(finalState.contentAsRooms().securityBannerState).isEqualTo(SecurityBannerState.None) From c8590f65ab06f9883f4e194797c655a2cc730c2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 08:29:31 +0000 Subject: [PATCH 35/57] Update dependency io.nlopez.compose.rules:detekt to v0.4.11 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 97daebb1d3..a9a0d72462 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,7 +61,7 @@ allprojects { config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { - detektPlugins("io.nlopez.compose.rules:detekt:0.4.10") + detektPlugins("io.nlopez.compose.rules:detekt:0.4.11") } // KtLint From a9ab32c8e367a107f60931804bbb64d89bb5abc4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 12:53:44 +0200 Subject: [PATCH 36/57] Rework: extract AvatarFactory and MediaRequestDataFactory from CoilMediaFetcher --- .../ui/media/AvatarDataFetcherFactory.kt | 42 +++++++++++++++++++ .../matrix/ui/media/CoilMediaFetcher.kt | 41 ------------------ .../matrix/ui/media/ImageLoaderFactories.kt | 4 +- .../media/MediaRequestDataFetcherFactory.kt | 41 ++++++++++++++++++ 4 files changed, 85 insertions(+), 43 deletions(-) create mode 100644 libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt create mode 100644 libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt new file mode 100644 index 0000000000..e6e71c3465 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.ui.media + +import android.content.Context +import coil.ImageLoader +import coil.fetch.Fetcher +import coil.request.Options +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrix.api.MatrixClient + +internal class AvatarDataFetcherFactory( + private val context: Context, + private val client: MatrixClient +) : Fetcher.Factory { + override fun create( + data: AvatarData, + options: Options, + imageLoader: ImageLoader + ): Fetcher { + return CoilMediaFetcher( + scalingFunction = { context.resources.displayMetrics.density * it }, + mediaLoader = client.mediaLoader, + mediaData = data.toMediaRequestData(), + options = options + ) + } +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index 455041aabd..b47f927d12 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -16,16 +16,12 @@ package io.element.android.libraries.matrix.ui.media -import android.content.Context -import coil.ImageLoader import coil.decode.DataSource import coil.decode.ImageSource import coil.fetch.FetchResult import coil.fetch.Fetcher import coil.fetch.SourceResult import coil.request.Options -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.toFile @@ -102,41 +98,4 @@ internal class CoilMediaFetcher( dataSource = DataSource.MEMORY ) } - - class MediaRequestDataFactory( - private val context: Context, - private val client: MatrixClient - ) : - Fetcher.Factory { - override fun create( - data: MediaRequestData, - options: Options, - imageLoader: ImageLoader - ): Fetcher { - return CoilMediaFetcher( - scalingFunction = { context.resources.displayMetrics.density * it }, - mediaLoader = client.mediaLoader, - mediaData = data, - options = options - ) - } - } - - class AvatarFactory( - private val context: Context, - private val client: MatrixClient - ) : Fetcher.Factory { - override fun create( - data: AvatarData, - options: Options, - imageLoader: ImageLoader - ): Fetcher { - return CoilMediaFetcher( - scalingFunction = { context.resources.displayMetrics.density * it }, - mediaLoader = client.mediaLoader, - mediaData = data.toMediaRequestData(), - options = options - ) - } - } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index 95b23f54a1..a3c1a0642a 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -52,8 +52,8 @@ class DefaultLoggedInImageLoaderFactory @Inject constructor( } add(AvatarDataKeyer()) add(MediaRequestDataKeyer()) - add(CoilMediaFetcher.AvatarFactory(context, matrixClient)) - add(CoilMediaFetcher.MediaRequestDataFactory(context, matrixClient)) + add(AvatarDataFetcherFactory(context, matrixClient)) + add(MediaRequestDataFetcherFactory(context, matrixClient)) } .build() } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt new file mode 100644 index 0000000000..a06a92ee3e --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.ui.media + +import android.content.Context +import coil.ImageLoader +import coil.fetch.Fetcher +import coil.request.Options +import io.element.android.libraries.matrix.api.MatrixClient + +internal class MediaRequestDataFetcherFactory( + private val context: Context, + private val client: MatrixClient +) : Fetcher.Factory { + override fun create( + data: MediaRequestData, + options: Options, + imageLoader: ImageLoader + ): Fetcher { + return CoilMediaFetcher( + scalingFunction = { context.resources.displayMetrics.density * it }, + mediaLoader = client.mediaLoader, + mediaData = data, + options = options + ) + } +} From cef31d370427e1deb57c975016eaba1b6f959bbc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 13:06:26 +0200 Subject: [PATCH 37/57] Log more errors. --- .../android/libraries/matrix/ui/media/CoilMediaFetcher.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index b47f927d12..78df94ef88 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -38,7 +38,9 @@ internal class CoilMediaFetcher( private val options: Options ) : Fetcher { override suspend fun fetch(): FetchResult? { - if (mediaData?.source == null) return null + if (mediaData?.source == null) return null.also { + Timber.e("MediaData source is null") + } return when (mediaData.kind) { is MediaRequestData.Kind.Content -> fetchContent(mediaData.source, options) is MediaRequestData.Kind.Thumbnail -> fetchThumbnail(mediaData.source, mediaData.kind, options) @@ -72,6 +74,8 @@ internal class CoilMediaFetcher( source = mediaSource, ).map { byteArray -> byteArray.asSourceResult(options) + }.onFailure { + Timber.e(it) }.getOrNull() } @@ -82,6 +86,8 @@ internal class CoilMediaFetcher( height = scalingFunction(kind.height.toFloat()).roundToLong(), ).map { byteArray -> byteArray.asSourceResult(options) + }.onFailure { + Timber.e(it) }.getOrNull() } From 34ba0a5e98ec2054f63a5310550c09ef172dab97 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 13:07:39 +0200 Subject: [PATCH 38/57] CoilMediaFetcher.mediaData cannot be null. --- .../android/libraries/matrix/ui/media/CoilMediaFetcher.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt index 78df94ef88..5f669571b4 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt @@ -34,12 +34,13 @@ import kotlin.math.roundToLong internal class CoilMediaFetcher( private val scalingFunction: (Float) -> Float, private val mediaLoader: MatrixMediaLoader, - private val mediaData: MediaRequestData?, + private val mediaData: MediaRequestData, private val options: Options ) : Fetcher { override suspend fun fetch(): FetchResult? { - if (mediaData?.source == null) return null.also { + if (mediaData.source == null) { Timber.e("MediaData source is null") + return null } return when (mediaData.kind) { is MediaRequestData.Kind.Content -> fetchContent(mediaData.source, options) From e35ba7af8f7facd5ca2b5b193da148910a3ba823 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 13:12:03 +0200 Subject: [PATCH 39/57] `AvatarData.toMediaRequestData()` can be internal. --- .../android/libraries/matrix/ui/media/AvatatarDataExt.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt index 39912cb443..0aa9330ff8 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatatarDataExt.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.media.MediaSource import kotlin.math.roundToLong -fun AvatarData.toMediaRequestData(): MediaRequestData { +internal fun AvatarData.toMediaRequestData(): MediaRequestData { return MediaRequestData( source = url?.let { MediaSource(it) }, kind = MediaRequestData.Kind.Thumbnail(size.dp.value.roundToLong()) From b9a1f05585ad63fcf495e0423388204e94cb4680 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:16:43 +0000 Subject: [PATCH 40/57] Update dependency com.posthog:posthog-android to v3.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 3e16152959..8e723a6eea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -187,7 +187,7 @@ zxing_cpp = "io.github.zxing-cpp:android:2.2.0" play_services_oss_licenses = "com.google.android.gms:play-services-oss-licenses:17.1.0" # Analytics -posthog = "com.posthog:posthog-android:3.6.0" +posthog = "com.posthog:posthog-android:3.6.1" sentry = "io.sentry:sentry-android:7.14.0" # main branch can be tested replacing the version with main-SNAPSHOT matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.1" From 61f9b20554654a2412a2ce6d15bcc63662848e50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 16:15:54 +0200 Subject: [PATCH 41/57] Ensure starting PinUnlockActivity does not crash the application. Fix regression from #3222 d4f8052696f69521c86f13115a703d854ac325c4 --- .../impl/unlock/di/PinUnlockBindings.kt | 4 +-- .../logout/impl/DefaultLogoutUseCase.kt | 28 +++++++-------- .../logout/impl/SessionLogoutModule.kt | 36 ------------------- 3 files changed, 15 insertions(+), 53 deletions(-) delete mode 100644 features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt index 0f71222ebd..ddd62d2fb6 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/di/PinUnlockBindings.kt @@ -18,9 +18,9 @@ package io.element.android.features.lockscreen.impl.unlock.di import com.squareup.anvil.annotations.ContributesTo import io.element.android.features.lockscreen.impl.unlock.activity.PinUnlockActivity -import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.di.AppScope -@ContributesTo(SessionScope::class) +@ContributesTo(AppScope::class) interface PinUnlockBindings { fun inject(activity: PinUnlockActivity) } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt index ffd8d11406..0ae58aa61e 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutUseCase.kt @@ -17,27 +17,25 @@ package io.element.android.features.logout.impl import com.squareup.anvil.annotations.ContributesBinding -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject import io.element.android.features.logout.api.LogoutUseCase import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.MatrixClientProvider -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import javax.inject.Inject -class DefaultLogoutUseCase @AssistedInject constructor( - @Assisted private val sessionId: String, +@ContributesBinding(AppScope::class) +class DefaultLogoutUseCase @Inject constructor( + private val authenticationService: MatrixAuthenticationService, private val matrixClientProvider: MatrixClientProvider, ) : LogoutUseCase { - @ContributesBinding(AppScope::class) - @AssistedFactory - interface Factory : LogoutUseCase.Factory { - override fun create(sessionId: String): DefaultLogoutUseCase - } - override suspend fun logout(ignoreSdkError: Boolean): String? { - val matrixClient = matrixClientProvider.getOrRestore(SessionId(sessionId)).getOrThrow() - val result = matrixClient.logout(ignoreSdkError = ignoreSdkError) - return result + val currentSession = authenticationService.getLatestSessionId() + return if (currentSession != null) { + matrixClientProvider.getOrRestore(currentSession) + .getOrThrow() + .logout(ignoreSdkError = true) + } else { + error("No session to sign out") + } } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt deleted file mode 100644 index ec725a34a6..0000000000 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/SessionLogoutModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2024 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.features.logout.impl - -import com.squareup.anvil.annotations.ContributesTo -import dagger.Module -import dagger.Provides -import io.element.android.features.logout.api.LogoutUseCase -import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder - -@Module -@ContributesTo(SessionScope::class) -object SessionLogoutModule { - @Provides - fun provideLogoutUseCase( - currentSessionIdHolder: CurrentSessionIdHolder, - factory: DefaultLogoutUseCase.Factory, - ): LogoutUseCase { - return factory.create(currentSessionIdHolder.current.value) - } -} From c41cb334108415ecbf25d9f219c9fb7aefbc1187 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 18:34:52 +0200 Subject: [PATCH 42/57] Provide distinct cache directory to the Rust SDK. --- .../impl/migrations/AppMigration06.kt | 47 ++++++++++++++++++ .../impl/reporter/DefaultBugReporterTest.kt | 1 + .../signedout/impl/SignedOutStateProvider.kt | 1 + .../libraries/matrix/impl/RustMatrixClient.kt | 26 ++++++---- .../matrix/impl/RustMatrixClientFactory.kt | 12 +++-- .../auth/RustMatrixAuthenticationService.kt | 35 +++++++------ .../libraries/matrix/impl/mapper/Session.kt | 6 ++- .../matrix/impl/paths/SessionPaths.kt | 37 ++++++++++++++ ...oryProvider.kt => SessionPathsProvider.kt} | 12 ++--- .../sessionstorage/api/SessionData.kt | 4 +- .../sessionstorage/impl/SessionDataMapper.kt | 2 + .../impl/src/main/sqldelight/databases/9.db | Bin 0 -> 12288 bytes .../libraries/matrix/session/SessionData.sq | 4 +- .../impl/src/main/sqldelight/migrations/8.sqm | 4 ++ .../android/samples/minimal/MainActivity.kt | 1 + 15 files changed, 153 insertions(+), 39 deletions(-) create mode 100644 features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPaths.kt rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/{SessionDirectoryProvider.kt => SessionPathsProvider.kt} (70%) create mode 100644 libraries/session-storage/impl/src/main/sqldelight/databases/9.db create mode 100644 libraries/session-storage/impl/src/main/sqldelight/migrations/8.sqm diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt new file mode 100644 index 0000000000..6672ae1f8a --- /dev/null +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.migration.impl.migrations + +import com.squareup.anvil.annotations.ContributesMultibinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.CacheDirectory +import io.element.android.libraries.sessionstorage.api.SessionStore +import java.io.File +import javax.inject.Inject + +/** + * Create the cache directory for the existing sessions. + */ +@ContributesMultibinding(AppScope::class) +class AppMigration06 @Inject constructor( + private val sessionStore: SessionStore, + @CacheDirectory private val cacheDirectory: File, +) : AppMigration { + override val order: Int = 6 + + override suspend fun migrate() { + val allSessions = sessionStore.getAllSessions() + for (session in allSessions) { + if (session.cachePath.isEmpty()) { + val sessionFile = File(session.sessionPath) + val sessionFolder = sessionFile.name + val cachePath = File(cacheDirectory, sessionFolder).absolutePath + sessionStore.updateData(session.copy(cachePath = cachePath)) + } + } + } +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt index fa78a164b1..18e78fb212 100755 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -289,6 +289,7 @@ class DefaultBugReporterTest { slidingSyncProxy = null, passphrase = null, sessionPath = "session", + cachePath = "cache", ) @Test fun `test sendBugReport error`() = runTest { 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 bd303f7945..33cbcd8f66 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 @@ -52,5 +52,6 @@ fun aSessionData( loginType = LoginType.UNKNOWN, passphrase = null, sessionPath = "/a/path/to/a/session", + cachePath = "/a/path/to/a/cache", ) } 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 3aab5bebed..f346100de1 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 @@ -56,6 +56,7 @@ import io.element.android.libraries.matrix.impl.media.RustMediaLoader 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.paths.getSessionPaths import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber @@ -67,7 +68,7 @@ import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper -import io.element.android.libraries.matrix.impl.util.SessionDirectoryProvider +import io.element.android.libraries.matrix.impl.util.SessionPathsProvider import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow @@ -161,7 +162,7 @@ class RustMatrixClient( sessionDispatcher = sessionDispatcher, ) - private val sessionDirectoryProvider = SessionDirectoryProvider(sessionStore) + private val sessionPathsProvider = SessionPathsProvider(sessionStore) private val isLoggingOut = AtomicBoolean(false) @@ -186,7 +187,7 @@ class RustMatrixClient( isTokenValid = false, loginType = existingData.loginType, passphrase = existingData.passphrase, - sessionPath = existingData.sessionPath, + sessionPaths = existingData.getSessionPaths(), ) sessionStore.updateData(newData) clientLog.d("Removed session data with access token: '$anonymizedAccessToken'.") @@ -217,7 +218,7 @@ class RustMatrixClient( isTokenValid = true, loginType = existingData.loginType, passphrase = existingData.passphrase, - sessionPath = existingData.sessionPath, + sessionPaths = existingData.getSessionPaths(), ) sessionStore.updateData(newData) clientLog.d("Saved new session data with access token: '$anonymizedAccessToken'.") @@ -617,16 +618,17 @@ class RustMatrixClient( private suspend fun File.getCacheSize( includeCryptoDb: Boolean = false, ): Long = withContext(sessionDispatcher) { - val sessionDirectory = sessionDirectoryProvider.provides(sessionId) ?: return@withContext 0L + val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L + val cacheSize = sessionDirectory.cacheDirectory.getSizeOfFiles() if (includeCryptoDb) { - sessionDirectory.getSizeOfFiles() + cacheSize + sessionDirectory.fileDirectory.getSizeOfFiles() } else { - listOf( + cacheSize + listOf( "matrix-sdk-state.sqlite3", "matrix-sdk-state.sqlite3-shm", "matrix-sdk-state.sqlite3-wal", ).map { fileName -> - File(sessionDirectory, fileName) + File(sessionDirectory.fileDirectory, fileName) }.sumOf { file -> file.length() } @@ -636,13 +638,15 @@ class RustMatrixClient( private suspend fun deleteSessionDirectory( deleteCryptoDb: Boolean = false, ): Boolean = withContext(sessionDispatcher) { - val sessionDirectory = sessionDirectoryProvider.provides(sessionId) ?: return@withContext false + val sessionPaths = sessionPathsProvider.provides(sessionId) ?: return@withContext false + // Always delete the cache directory + sessionPaths.cacheDirectory.deleteRecursively() if (deleteCryptoDb) { // Delete the folder and all its content - sessionDirectory.deleteRecursively() + sessionPaths.fileDirectory.deleteRecursively() } else { // Delete only the state.db file - sessionDirectory.listFiles().orEmpty() + sessionPaths.fileDirectory.listFiles().orEmpty() .filter { it.name.contains("matrix-sdk-state") } .forEach { file -> Timber.w("Deleting file ${file.name}...") 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 a3304dc5bf..2dfb215116 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 @@ -20,6 +20,8 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider +import io.element.android.libraries.matrix.impl.paths.SessionPaths +import io.element.android.libraries.matrix.impl.paths.getSessionPaths import io.element.android.libraries.matrix.impl.proxy.ProxyProvider import io.element.android.libraries.matrix.impl.util.anonymizedTokens import io.element.android.libraries.network.useragent.UserAgentProvider @@ -52,7 +54,7 @@ class RustMatrixClientFactory @Inject constructor( ) { suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { val client = getBaseClientBuilder( - sessionPath = sessionData.sessionPath, + sessionPaths = sessionData.getSessionPaths(), passphrase = sessionData.passphrase, slidingSync = if (appPreferencesStore.isSimplifiedSlidingSyncEnabledFlow().first()) { ClientBuilderSlidingSync.Simplified @@ -87,14 +89,16 @@ class RustMatrixClientFactory @Inject constructor( } internal fun getBaseClientBuilder( - sessionPath: String, + sessionPaths: SessionPaths, passphrase: String?, slidingSyncProxy: String? = null, slidingSync: ClientBuilderSlidingSync, ): ClientBuilder { return ClientBuilder() - // TODO SDK claims it's valid to use the same path for data and cache, but would be better to use different paths - .sessionPaths(dataPath = sessionPath, cachePath = sessionPath) + .sessionPaths( + dataPath = sessionPaths.fileDirectory.absolutePath, + cachePath = sessionPaths.cacheDirectory.absolutePath, + ) .passphrase(passphrase) .slidingSyncProxy(slidingSyncProxy) .userAgent(userAgentProvider.provide()) 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 b447dd584b..c373172b31 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 @@ -21,6 +21,7 @@ import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -31,6 +32,7 @@ import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory +import io.element.android.libraries.matrix.impl.paths.SessionPaths 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 @@ -61,6 +63,7 @@ import javax.inject.Inject @SingleIn(AppScope::class) class RustMatrixAuthenticationService @Inject constructor( private val baseDirectory: File, + @CacheDirectory private val cacheDirectory: File, private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, private val rustMatrixClientFactory: RustMatrixClientFactory, @@ -73,14 +76,18 @@ class RustMatrixAuthenticationService @Inject constructor( // 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. - private var sessionPath: File? = null + private var sessionPaths: SessionPaths? = null private var currentClient: Client? = null private var currentHomeserver = MutableStateFlow(null) - private fun rotateSessionPath(): File { - sessionPath?.deleteRecursively() - return File(baseDirectory, UUID.randomUUID().toString()) - .also { sessionPath = it } + private fun rotateSessionPath(): SessionPaths { + sessionPaths?.deleteRecursively() + val subPath = UUID.randomUUID().toString() + return SessionPaths( + fileDirectory = File(baseDirectory, subPath), + cacheDirectory = File(cacheDirectory, subPath), + ) + .also { sessionPaths = it } } override fun loggedInStateFlow(): Flow { @@ -145,14 +152,14 @@ class RustMatrixAuthenticationService @Inject constructor( withContext(coroutineDispatchers.io) { runCatching { val client = currentClient ?: error("You need to call `setHomeserver()` first") - val currentSessionPath = sessionPath ?: error("You need to call `setHomeserver()` first") + val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") client.login(username, password, "Element X Android", null) val sessionData = client.session() .toSessionData( isTokenValid = true, loginType = LoginType.PASSWORD, passphrase = pendingPassphrase, - sessionPath = currentSessionPath.absolutePath, + sessionPaths = currentSessionPaths, ) clear() sessionStore.storeData(sessionData) @@ -196,14 +203,14 @@ class RustMatrixAuthenticationService @Inject constructor( return withContext(coroutineDispatchers.io) { runCatching { val client = currentClient ?: error("You need to call `setHomeserver()` first") - val currentSessionPath = sessionPath ?: error("You need to call `setHomeserver()` first") + val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first") client.loginWithOidcCallback(urlForOidcLogin, callbackUrl) val sessionData = client.session().toSessionData( isTokenValid = true, loginType = LoginType.OIDC, passphrase = pendingPassphrase, - sessionPath = currentSessionPath.absolutePath, + sessionPaths = currentSessionPaths, ) clear() pendingOidcAuthorizationData?.close() @@ -218,10 +225,10 @@ class RustMatrixAuthenticationService @Inject constructor( override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) = withContext(coroutineDispatchers.io) { - val emptySessionPath = rotateSessionPath() + val emptySessionPaths = rotateSessionPath() runCatching { val client = rustMatrixClientFactory.getBaseClientBuilder( - sessionPath = emptySessionPath.absolutePath, + sessionPaths = emptySessionPaths, passphrase = pendingPassphrase, slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL, slidingSync = ClientBuilderSlidingSync.Discovered, @@ -242,7 +249,7 @@ class RustMatrixAuthenticationService @Inject constructor( isTokenValid = true, loginType = LoginType.QR, passphrase = pendingPassphrase, - sessionPath = emptySessionPath.absolutePath, + sessionPaths = emptySessionPaths, ) sessionStore.storeData(sessionData) SessionId(sessionData.userId) @@ -262,10 +269,10 @@ class RustMatrixAuthenticationService @Inject constructor( } private fun getBaseClientBuilder( - sessionPath: File, + sessionPaths: SessionPaths, ) = rustMatrixClientFactory .getBaseClientBuilder( - sessionPath = sessionPath.absolutePath, + sessionPaths = sessionPaths, passphrase = pendingPassphrase, slidingSyncProxy = AuthenticationConfig.SLIDING_SYNC_PROXY_URL, slidingSync = ClientBuilderSlidingSync.Discovered, 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 402f86ce4f..280d76691c 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 @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.mapper +import io.element.android.libraries.matrix.impl.paths.SessionPaths import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionData import org.matrix.rustcomponents.sdk.Session @@ -25,7 +26,7 @@ internal fun Session.toSessionData( isTokenValid: Boolean, loginType: LoginType, passphrase: String?, - sessionPath: String, + sessionPaths: SessionPaths, homeserverUrl: String? = null, ) = SessionData( userId = userId, @@ -39,5 +40,6 @@ internal fun Session.toSessionData( isTokenValid = isTokenValid, loginType = loginType, passphrase = passphrase, - sessionPath = sessionPath, + sessionPath = sessionPaths.fileDirectory.absolutePath, + cachePath = sessionPaths.cacheDirectory.absolutePath, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPaths.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPaths.kt new file mode 100644 index 0000000000..0634f3f935 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPaths.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.paths + +import io.element.android.libraries.sessionstorage.api.SessionData +import java.io.File + +data class SessionPaths( + val fileDirectory: File, + val cacheDirectory: File, +) { + fun deleteRecursively() { + fileDirectory.deleteRecursively() + cacheDirectory.deleteRecursively() + } +} + +internal fun SessionData.getSessionPaths(): SessionPaths { + return SessionPaths( + fileDirectory = File(sessionPath), + cacheDirectory = File(cachePath), + ) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryProvider.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionPathsProvider.kt similarity index 70% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryProvider.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionPathsProvider.kt index f9b8edbd8e..63b01f4c88 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionDirectoryProvider.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/SessionPathsProvider.kt @@ -17,15 +17,15 @@ package io.element.android.libraries.matrix.impl.util import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.impl.paths.SessionPaths +import io.element.android.libraries.matrix.impl.paths.getSessionPaths import io.element.android.libraries.sessionstorage.api.SessionStore -import java.io.File -import javax.inject.Inject -class SessionDirectoryProvider @Inject constructor( +class SessionPathsProvider( private val sessionStore: SessionStore, ) { - suspend fun provides(sessionId: SessionId): File? { - val path = sessionStore.getSession(sessionId.value)?.sessionPath ?: return null - return File(path) + suspend fun provides(sessionId: SessionId): SessionPaths? { + val sessionData = sessionStore.getSession(sessionId.value) ?: return null + return sessionData.getSessionPaths() } } 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 30f721d68a..71504ff562 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 @@ -44,6 +44,8 @@ data class SessionData( val loginType: LoginType, /** The optional passphrase used to encrypt data in the SDK local store. */ val passphrase: String?, - /** The path to the session data stored in the filesystem. */ + /** The paths to the session data stored in the filesystem. */ val sessionPath: String, + /** The path to the cache data stored for the session in the filesystem. */ + val cachePath: String, ) 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 026c13eadc..1033286807 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 @@ -35,6 +35,7 @@ internal fun SessionData.toDbModel(): DbSessionData { loginType = loginType.name, passphrase = passphrase, sessionPath = sessionPath, + cachePath = cachePath, ) } @@ -52,5 +53,6 @@ internal fun DbSessionData.toApiModel(): SessionData { loginType = LoginType.fromName(loginType ?: LoginType.UNKNOWN.name), passphrase = passphrase, sessionPath = sessionPath, + cachePath = cachePath, ) } diff --git a/libraries/session-storage/impl/src/main/sqldelight/databases/9.db b/libraries/session-storage/impl/src/main/sqldelight/databases/9.db new file mode 100644 index 0000000000000000000000000000000000000000..906384dd8e775befd8bbca2d1d2748384b7c8603 GIT binary patch literal 12288 zcmeI#O;6h}7zc2tOdG0H0Vl4H=mn%PiML7IC=gIZ#=15|^i-L|G}h82#G#^hF7U1R zRy)piR8gj}oLBuVIgR7TkL_R5gL^+bSqQpK^D!@IkF7GtVMj!aF-O-WU1!C#Kg=5X z*R-pO!@f+O8!Nw9tubJY!OBb30Q5ru0uX=z1Rwwb2tWV=5cux|9)}9 z@YzO%Vj=_aXI$CGgFw&hr$0sVHci=`i`!hN=(WFm9HnESr=EzspT~b2r(zk(WYn7` zK_^cirZ%P=yS+_33`IyXA)UbNns)6v<8&kwU+UBaA7|9Q^xbo}`~IjsJ1Ui7VAv|A z*tfy5-0BtA>zhsRDtGFhwfdeaXI!Z)%DJiv|7yck`NBKAh~5Iy zY!w?D?{EFk<_LHYiGOCOZynS=uD6>GlSwEZ)LpDk^Fw}Lq_#br`=o|Da}9H@UpD`# zhPkYo3kn1v009U<00Izz00bZa0SG_<0*fk8HygwGe^FmA_5}e5KmY;|fB*y_009U< K00I!O0>1%Q0ngU} literal 0 HcmV?d00001 diff --git a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq index 74f268606a..6e4c817475 100644 --- a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq +++ b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq @@ -25,7 +25,9 @@ CREATE TABLE SessionData ( -- added in version 5 passphrase TEXT, -- added in version 6 - sessionPath TEXT NOT NULL DEFAULT "" + sessionPath TEXT NOT NULL DEFAULT "", + -- added in version 9 + cachePath TEXT NOT NULL DEFAULT "" ); diff --git a/libraries/session-storage/impl/src/main/sqldelight/migrations/8.sqm b/libraries/session-storage/impl/src/main/sqldelight/migrations/8.sqm new file mode 100644 index 0000000000..02a8d438ed --- /dev/null +++ b/libraries/session-storage/impl/src/main/sqldelight/migrations/8.sqm @@ -0,0 +1,4 @@ +-- Migrate DB from version 8 +-- Add cachePath so we can track the anonymized path for the session cache dir + +ALTER TABLE SessionData ADD COLUMN cachePath TEXT NOT NULL DEFAULT ""; diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index cab4c09660..68273a088e 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -50,6 +50,7 @@ class MainActivity : ComponentActivity() { val proxyProvider = NoOpProxyProvider() RustMatrixAuthenticationService( baseDirectory = baseDirectory, + cacheDirectory = applicationContext.cacheDir, coroutineDispatchers = Singleton.coroutineDispatchers, sessionStore = sessionStore, rustMatrixClientFactory = RustMatrixClientFactory( From 8b65cf2f4b9a0c7ff7e41b6d5660226847728d42 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 30 Aug 2024 18:42:00 +0200 Subject: [PATCH 43/57] Introduce SessionPathsFactory. --- .../auth/RustMatrixAuthenticationService.kt | 15 +++----- .../matrix/impl/paths/SessionPathsFactory.kt | 35 +++++++++++++++++++ .../android/samples/minimal/MainActivity.kt | 4 +-- 3 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPathsFactory.kt 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 c373172b31..5459afea6c 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 @@ -21,7 +21,6 @@ import io.element.android.appconfig.AuthenticationConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.di.AppScope -import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -32,13 +31,14 @@ import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync import io.element.android.libraries.matrix.impl.RustMatrixClientFactory -import io.element.android.libraries.matrix.impl.paths.SessionPaths 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.mapper.toSessionData +import io.element.android.libraries.matrix.impl.paths.SessionPaths +import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.LoginType import io.element.android.libraries.sessionstorage.api.SessionStore @@ -55,15 +55,12 @@ import org.matrix.rustcomponents.sdk.QrLoginProgressListener import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk.OidcAuthorizationData -import java.io.File -import java.util.UUID import javax.inject.Inject @ContributesBinding(AppScope::class) @SingleIn(AppScope::class) class RustMatrixAuthenticationService @Inject constructor( - private val baseDirectory: File, - @CacheDirectory private val cacheDirectory: File, + private val sessionPathsFactory: SessionPathsFactory, private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, private val rustMatrixClientFactory: RustMatrixClientFactory, @@ -82,11 +79,7 @@ class RustMatrixAuthenticationService @Inject constructor( private fun rotateSessionPath(): SessionPaths { sessionPaths?.deleteRecursively() - val subPath = UUID.randomUUID().toString() - return SessionPaths( - fileDirectory = File(baseDirectory, subPath), - cacheDirectory = File(cacheDirectory, subPath), - ) + return sessionPathsFactory.create() .also { sessionPaths = it } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPathsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPathsFactory.kt new file mode 100644 index 0000000000..b901ba91d1 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/paths/SessionPathsFactory.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.paths + +import io.element.android.libraries.di.CacheDirectory +import java.io.File +import java.util.UUID +import javax.inject.Inject + +class SessionPathsFactory @Inject constructor( + private val baseDirectory: File, + @CacheDirectory private val cacheDirectory: File, +) { + fun create(): SessionPaths { + val subPath = UUID.randomUUID().toString() + return SessionPaths( + fileDirectory = File(baseDirectory, subPath), + cacheDirectory = File(cacheDirectory, subPath), + ) + } +} diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index 68273a088e..c1eb3487f2 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.auth.OidcConfigurationProvider import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService +import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.network.useragent.SimpleUserAgentProvider import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.sessionstorage.api.LoggedInState @@ -49,8 +50,7 @@ class MainActivity : ComponentActivity() { val userCertificatesProvider = NoOpUserCertificatesProvider() val proxyProvider = NoOpProxyProvider() RustMatrixAuthenticationService( - baseDirectory = baseDirectory, - cacheDirectory = applicationContext.cacheDir, + sessionPathsFactory = SessionPathsFactory(baseDirectory, applicationContext.cacheDir), coroutineDispatchers = Singleton.coroutineDispatchers, sessionStore = sessionStore, rustMatrixClientFactory = RustMatrixClientFactory( From 5ad2764402375a9d8c95bec0a1de6412e153c3b0 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:15:17 +0000 Subject: [PATCH 44/57] Sync Strings from Localazy --- .../src/main/res/values-pt/translations.xml | 2 +- .../src/main/res/values-de/translations.xml | 2 +- .../src/main/res/values-uz/translations.xml | 2 +- .../src/main/res/values-uz/translations.xml | 2 +- .../src/main/res/values-cs/translations.xml | 2 + .../src/main/res/values-et/translations.xml | 2 + .../src/main/res/values-hu/translations.xml | 2 + .../src/main/res/values-sk/translations.xml | 2 + .../src/main/res/values-sv/translations.xml | 2 + .../src/main/res/values-be/translations.xml | 1 - .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-et/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 2 + .../src/main/res/values-hu/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 1 - .../src/main/res/values-pl/translations.xml | 1 - .../src/main/res/values-pt/translations.xml | 1 - .../src/main/res/values-ru/translations.xml | 1 - .../src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-sv/translations.xml | 2 +- .../src/main/res/values-uk/translations.xml | 1 - .../src/main/res/values-zh/translations.xml | 1 - .../impl/src/main/res/values/localazy.xml | 4 +- .../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-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-fr/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-ka/translations.xml | 1 + .../src/main/res/values-nl/translations.xml | 1 + .../src/main/res/values-pl/translations.xml | 1 + .../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-uk/translations.xml | 1 + .../src/main/res/values-uz/translations.xml | 1 + .../main/res/values-zh-rTW/translations.xml | 1 + .../src/main/res/values-zh/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-sv/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 3 + .../src/main/res/values-sv/translations.xml | 3 + .../src/main/res/values-uz/translations.xml | 6 +- screenshots/html/data.js | 1166 +++++++++-------- 55 files changed, 644 insertions(+), 603 deletions(-) diff --git a/features/call/impl/src/main/res/values-pt/translations.xml b/features/call/impl/src/main/res/values-pt/translations.xml index 04568a1219..58ee9d523f 100644 --- a/features/call/impl/src/main/res/values-pt/translations.xml +++ b/features/call/impl/src/main/res/values-pt/translations.xml @@ -3,5 +3,5 @@ "Chamada em curso" "Toca para voltar à chamada" "☎️ Chamada em curso" - "A receber chamada da Element " + "A receber chamada da Element" 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 01f0749846..7804663579 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -37,7 +37,7 @@ Wenn du fortfährst, können sich einige deiner Einstellungen ändern." "Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." "Alle Nachrichten" "Nur Erwähnungen und Schlüsselwörter" - "Benachrichtige mich bei Direktnachrichten über " + "Benachrichtige mich bei Direktnachrichten über" "Bei Gruppenchats benachrichtige mich bei" "Benachrichtigungen auf diesem Gerät aktivieren" "Die Konfiguration wurde nicht korrigiert, bitte versuche es erneut." diff --git a/features/rageshake/impl/src/main/res/values-uz/translations.xml b/features/rageshake/impl/src/main/res/values-uz/translations.xml index 474c775cb0..a1abfd64a0 100644 --- a/features/rageshake/impl/src/main/res/values-uz/translations.xml +++ b/features/rageshake/impl/src/main/res/values-uz/translations.xml @@ -9,7 +9,7 @@ "Iloji bo\'lsa, tavsifni ingliz tilida yozing." "Buzilish jurnallarini yuboring" "Jurnallarga ruxsat bering" - "Ekran tasvirini yuboring " + "Ekran tasvirini yuboring" "Har bir narsa to\'ri ishlayotganiga ishonch hosil qilish uchun xabaringizga jurnallar kiritiladi. Xabarni jurnallarsiz yuborish uchun ushbu sozlamani oʻchiring." "%1$soxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko\'rmoqchimisiz?" 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 a7d0642bed..030e0a91bd 100644 --- a/features/roomdetails/impl/src/main/res/values-uz/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uz/translations.xml @@ -14,7 +14,7 @@ "Bu xona ovozini o‘chirib bo‘lmadi, qayta urinib ko‘ring." "Bu xonaning ovozi yoqilmadi, qayta urinib ko‘ring." "Odamlarni taklif qiling" - "Xonani tark etish " + "Xonani tark etish" "Maxsus" "Standart" "Bildirishnomalar" diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml index f3dc0bda9f..69779542a9 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -1,5 +1,7 @@ + "Vygenerujte nový klíč pro obnovení, který lze použít k obnovení historie šifrovaných zpráv v případě, že ztratíte přístup ke svým zařízením." + "Nastavení obnovy" "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení." "Potvrďte klíč pro obnovení" "Abyste nikdy nezmeškali důležitý hovor, změňte nastavení tak, abyste povolili oznámení na celé obrazovce, když je telefon uzamčen." diff --git a/features/roomlist/impl/src/main/res/values-et/translations.xml b/features/roomlist/impl/src/main/res/values-et/translations.xml index 3bd8c87d29..e64e0fc6e0 100644 --- a/features/roomlist/impl/src/main/res/values-et/translations.xml +++ b/features/roomlist/impl/src/main/res/values-et/translations.xml @@ -1,5 +1,7 @@ + "Loo uus taastevõti, mida saad kasutada oma krüptitud sõnumite ajaloo taastamisel olukorras, kus kaotad ligipääsu oma seadmetele." + "Seadista taastamine" "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti." "Sisesta oma taastevõti" "Selleks, et sul ainsamgi tähtis kõne ei jääks märkamata, siis palun muuda oma nutiseadme seadistusi nii, et lukustusvaates oleksid täisekraani mõõtu teavitused." diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index 477c666356..2325b7ae21 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -1,5 +1,7 @@ + "Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést." + "Helyreállítás beállítása" "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez." "Helyreállítási kulcs megerősítése" "Annak érdekében, hogy soha ne maradjon le egyetlen fontos hívásról sem, módosítsa a beállításokat, hogy engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van." diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index 3bbb65165a..3d6e215cc6 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -1,5 +1,7 @@ + "Vytvorte nový kľúč na obnovenie, ktorý môžete použiť na obnovenie vašej histórie šifrovaných správ v prípade straty prístupu k vašim zariadeniam." + "Nastaviť obnovenie" "Vaša záloha konverzácie nie je momentálne synchronizovaná. Na zachovanie prístupu k zálohe konverzácie musíte potvrdiť svoj kľúč na obnovu." "Potvrďte svoj kľúč na obnovenie" "Aby ste už nikdy nezmeškali dôležitý hovor, zmeňte svoje nastavenia a povoľte upozornenia na celú obrazovku, keď je váš telefón uzamknutý." diff --git a/features/roomlist/impl/src/main/res/values-sv/translations.xml b/features/roomlist/impl/src/main/res/values-sv/translations.xml index 4f7b26bb52..529271c97a 100644 --- a/features/roomlist/impl/src/main/res/values-sv/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sv/translations.xml @@ -1,5 +1,7 @@ + "Skapa en ny återställningsnyckel som kan användas för att återställa din krypterade meddelandehistorik om du förlorar åtkomst till dina enheter." + "Ställ in återställning" "Din chattsäkerhetskopia är för närvarande inte synkroniserad. Du måste ange din återställningsnyckel för att behålla åtkomsten till din chattsäkerhetskopia." "Ange din återställningsnyckel" "För att säkerställa att du aldrig missar ett viktigt samtal, ändra dina inställningar för att tillåta helskärmsmeddelanden när telefonen är låst." diff --git a/features/securebackup/impl/src/main/res/values-be/translations.xml b/features/securebackup/impl/src/main/res/values-be/translations.xml index db237bbe19..75e4c2ccb9 100644 --- a/features/securebackup/impl/src/main/res/values-be/translations.xml +++ b/features/securebackup/impl/src/main/res/values-be/translations.xml @@ -20,7 +20,6 @@ "Вы страціце існуючую гісторыю паведамленняў" "Вам трэба будзе зноў запэўніць ўсе вашы існуючыя прылады і кантакты" "Працягвайце, толькі калі вы ўпэўненыя, што страцілі ўсе астатнія прылады і ключ аднаўлення." - "Калі вы не ўвайшлі ў сістэму на іншых прыладах і страцілі ключ аднаўлення, вам неабходна скінуць ключы пацверджання, каб працягнуць выкарыстанне прыкладання." "Скіньце ключы пацверджання, калі вы не можаце пацвердзіць яго іншым спосабам" "Адключыць" "Вы страціце зашыфраваныя паведамленні, калі выйдзеце з усіх прылад." diff --git a/features/securebackup/impl/src/main/res/values-cs/translations.xml b/features/securebackup/impl/src/main/res/values-cs/translations.xml index 442fa42630..6425f52b66 100644 --- a/features/securebackup/impl/src/main/res/values-cs/translations.xml +++ b/features/securebackup/impl/src/main/res/values-cs/translations.xml @@ -16,11 +16,11 @@ "Postupujte podle pokynů k vytvoření nového obnovovacího klíče" "Uložte nový klíč pro obnovení do správce hesel nebo do zašifrované poznámky" "Obnovte šifrování účtu pomocí jiného zařízení" + "Pokračovat v resetování" "Podrobnosti o vašem účtu, kontaktech, preferencích a seznamu chatu budou zachovány" "Ztratíte svou stávající historii zpráv" "Budete muset znovu ověřit všechna stávající zařízení a kontakty" "Obnovte svou identitu pouze v případě, že nemáte přístup k jinému přihlášenému zařízení a ztratili jste klíč pro obnovení." - "Pokud nejste přihlášeni k žádnému jinému zařízení a ztratili jste klíč pro obnovení, budete muset obnovit svou identitu, abyste mohli pokračovat v používání aplikace. " "Obnovte svou identitu v případě, že nemůžete potvrdit jiným způsobem" "Vypnout" "Pokud se odhlásíte ze všech zařízení, přijdete o zašifrované zprávy." 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 cec56f5789..2c3aa2d12b 100644 --- a/features/securebackup/impl/src/main/res/values-et/translations.xml +++ b/features/securebackup/impl/src/main/res/values-et/translations.xml @@ -16,11 +16,11 @@ "Uue taastevõtme loomiseks palun järgi juhendit" "Salvesta oma uus taastevõti kas salasõnahalduris, krüptitud failis või mõnel muul turvalisel viisil" "Lähtesta oma konto krüptimine mõnest muust oma seadmest" + "Jätka lähtestamisega" "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 soovid jätkata selle rakenduse kasutamist ja sa pole mitte üheski seadmes sisse logitud ning oled kaotanud oma taastevõtme, siis tõesti pead lähtestama oma identiteedi. " "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." diff --git a/features/securebackup/impl/src/main/res/values-fr/translations.xml b/features/securebackup/impl/src/main/res/values-fr/translations.xml index b0eb80b541..9b1276f654 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -19,6 +19,7 @@ "Les détails de votre compte, vos contacts, vos préférences et votre liste de discussions seront conservés" "Vous perdrez l’historique de vos messages" "Vous devrez vérifier à nouveau tous vos appareils et tous vos contacts" + "Ne réinitialisez votre identité que si vous n’avez plus accès à aucune autre session et que vous avez perdu votre clé de récupération." "Désactiver" "Vous perdrez vos messages chiffrés si vous vous déconnectez de toutes vos sessions." "Êtes-vous certain de vouloir désactiver la sauvegarde?" @@ -58,5 +59,6 @@ "Cette opération ne peut pas être annulée." "Une erreur s’est produite. Vérifiez que le mot de passe de votre compte est correct et réessayez." "Saisissez…" + "Veuillez confirmec que vous souhaitez réinitialiser votre identité." "Saisissez le mot de passe de votre compte pour continuer" diff --git a/features/securebackup/impl/src/main/res/values-hu/translations.xml b/features/securebackup/impl/src/main/res/values-hu/translations.xml index 312ad0705c..20575540b7 100644 --- a/features/securebackup/impl/src/main/res/values-hu/translations.xml +++ b/features/securebackup/impl/src/main/res/values-hu/translations.xml @@ -16,11 +16,11 @@ "Kövesse az utasításokat egy új helyreállítási kulcs létrehozásához" "Mentse az új helyreállítási kulcsot egy jelszókezelőbe vagy egy titkosított jegyzetbe." "A fiók titkosításának visszaállítása egy másik eszköz használatával" + "Visszaállítás folytatása" "A fiókadatok, a kapcsolatok, a beállítások és a csevegéslista megmarad" "Elveszíti meglévő üzenetelőzményeit" "Újból ellenőriznie kell az összes meglévő eszközét és csevegőpartnerét" "Csak akkor állítsa vissza a személyazonosságát, ha nem fér hozzá másik bejelentkezett eszközhöz, és elvesztette a helyreállítási kulcsot." - "Ha nincs bejelentkezve más eszközre, és elvesztette a helyreállítási kulcsot, akkor az alkalmazás használatának folytatásához vissza kell állítania személyazonosságát. " "Állítsa vissza a személyazonosságát, ha más módon nem tudja megerősíteni" "Kikapcsolás" "Ha kijelentkezik az összes eszközéről, akkor elveszti a titkosított üzeneteit." diff --git a/features/securebackup/impl/src/main/res/values-it/translations.xml b/features/securebackup/impl/src/main/res/values-it/translations.xml index cbeadcc2e1..c1d67299ba 100644 --- a/features/securebackup/impl/src/main/res/values-it/translations.xml +++ b/features/securebackup/impl/src/main/res/values-it/translations.xml @@ -20,7 +20,6 @@ "Perderai la cronologia dei messaggi esistente" "Dovrai verificare nuovamente tutti i dispositivi e i contatti esistenti" "Reimposta la tua identità solo se non hai accesso a un altro dispositivo su cui hai effettuato l\'accesso e hai perso la chiave di recupero." - "Se non hai effettuato l\'accesso su nessun altro dispositivo e hai perso la chiave di recupero, dovrai reimpostare la tua identità per continuare a utilizzare l\'app." "Reimposta la tua identità nel caso in cui non riesci a confermare in un altro modo" "Disattiva" "Perderai i tuoi messaggi cifrati se sei disconnesso da tutti i dispositivi." 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 749fea102a..eea550627f 100644 --- a/features/securebackup/impl/src/main/res/values-pl/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pl/translations.xml @@ -20,7 +20,6 @@ "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." - "Jeśli nie jesteś zalogowany na żadnym innym urządzeniu i straciłeś swój klucz przywracania, musisz zresetować swoją tożsamość, aby kontynuować korzystanie z aplikacji. " "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." 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 27d41c98c2..f93e5dedf4 100644 --- a/features/securebackup/impl/src/main/res/values-pt/translations.xml +++ b/features/securebackup/impl/src/main/res/values-pt/translations.xml @@ -20,7 +20,6 @@ "Perderás o acesso ao teu histórico de mensagens existente" "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." - "Se não tiveres sessão iniciada em nenhum outro dispositivo e perdeste o acesso à tua chave de recuperação, precisarás de repor a tua identidade para continuares a usar a aplicaçã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." diff --git a/features/securebackup/impl/src/main/res/values-ru/translations.xml b/features/securebackup/impl/src/main/res/values-ru/translations.xml index c02d05952d..8866dc9613 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -30,7 +30,6 @@ "Вы потеряете существующую историю сообщений" "Вам нужно будет заново подтвердить все существующие устройства и контакты." "Сбрасывайте данные только в том случае, если у вас нет доступа к другому устройству, на котором выполнен вход, и вы потеряли ключ восстановления." - "Если вы не вошли в систему на других устройствах и потеряли ключ восстановления, вам необходимо сбросить учетные данные, чтобы продолжить использование приложения. " "Сбросьте ключи подтверждения, если вы не можете подтвердить свою личность другим способом." "Выключить" "Вы потеряете зашифрованные сообщения, если выйдете из всех устройств." diff --git a/features/securebackup/impl/src/main/res/values-sk/translations.xml b/features/securebackup/impl/src/main/res/values-sk/translations.xml index 7d15dd352a..8a088f4245 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -16,11 +16,11 @@ "Postupujte podľa pokynov na vytvorenie nového kľúča na obnovenie" "Uložte si nový kľúč na obnovenie do správcu hesiel alebo do zašifrovanej poznámky" "Obnovte šifrovanie vášho účtu pomocou iného zariadenia" + "Pokračovať v obnovovaní" "Údaje o vašom účte, kontakty, predvoľby a zoznam konverzácií budú zachované" "Stratíte svoju existujúcu históriu správ" "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." - "Ak nie ste prihlásení do žiadneho iného zariadenia a stratili ste kľúč na obnovenie, budete musieť znovu obnoviť svoju identitu, aby ste mohli pokračovať v používaní aplikácie. " "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í" diff --git a/features/securebackup/impl/src/main/res/values-sv/translations.xml b/features/securebackup/impl/src/main/res/values-sv/translations.xml index 58a8c8571b..3599320cd8 100644 --- a/features/securebackup/impl/src/main/res/values-sv/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sv/translations.xml @@ -16,11 +16,11 @@ "Följ anvisningarna för att skapa en ny återställningsnyckel" "Spara din nya återställningsnyckel i en lösenordshanterare eller krypterad anteckning" "Återställ krypteringen för ditt konto med en annan enhet" + "Fortsätt återställning" "Dina kontouppgifter, kontakter, inställningar och chattlistor kommer bevaras" "Du kommer att förlora din befintliga meddelandehistorik" "Du måste verifiera alla dina befintliga enheter och kontakter igen" "Återställ bara din identitet om du inte har tillgång till en annan inloggad enhet och du har tappat bort din återställningsnyckel." - "Om du inte är inloggad på någon annan enhet och du har tappat bort din återställningsnyckel måste du återställa din identitet för att fortsätta använda appen. " "Återställ din identitet ifall du inte kan bekräfta på annat sätt" "Stäng av" "Du kommer att förlora dina krypterade meddelanden om du loggas ut från alla enheter." 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 98288f1aa6..14e47f8bad 100644 --- a/features/securebackup/impl/src/main/res/values-uk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-uk/translations.xml @@ -20,7 +20,6 @@ "Ви втратите свою наявну історію повідомлень" "Вам доведеться підтвердити всі наявні пристрої та контакти знову" "Скидайте ідентичність тільки якщо ви не маєте доступу до інших пристроїв в обліковому записі та втратили свій ключ відновлення." - "Якщо ви не увійшли на інших пристроях та втратили свій ключ відновлення, то вам доведеться скинути свою ідентичність, щоб продовжити використовувати застосунок. " "Скиньте свою ідентичність, якщо не можете підтвердити іншим способом" "Вимкнути" "Ви втратите зашифровані повідомлення, якщо вийдете з усіх пристроїв." diff --git a/features/securebackup/impl/src/main/res/values-zh/translations.xml b/features/securebackup/impl/src/main/res/values-zh/translations.xml index f350d6ff62..0b4f12b17a 100644 --- a/features/securebackup/impl/src/main/res/values-zh/translations.xml +++ b/features/securebackup/impl/src/main/res/values-zh/translations.xml @@ -20,7 +20,6 @@ "您将丢失现有的消息历史记录" "您将需要再次验证所有您的现有设备和联系人" "仅当您无法访问其他已登录设备并且丢失了恢复密钥时才重置您的身份。" - "如果您未登录任何其他设备,并且丢失了恢复密钥,则需要重置身份才能继续使用该应用。" "如果您无法通过其他方式确认,请重置您的身份" "关闭" "如果您登出所有设备,您的加密消息将丢失。" diff --git a/features/securebackup/impl/src/main/res/values/localazy.xml b/features/securebackup/impl/src/main/res/values/localazy.xml index 1a38c905bf..f4795c23da 100644 --- a/features/securebackup/impl/src/main/res/values/localazy.xml +++ b/features/securebackup/impl/src/main/res/values/localazy.xml @@ -16,12 +16,12 @@ "Follow the instructions to create a new recovery key" "Save your new recovery key in a password manager or encrypted note" "Reset the encryption for your account using another device" + "Continue reset" "Your account details, contacts, preferences, and chat list will be kept" "You will lose your existing message history" "You will need to verify all your existing devices and contacts again" "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key." - "If you’re not signed in to any other devices and you’ve lost your recovery key, then you’ll need to reset your identity to continue using the app." - "Reset your identity in case you can’t confirm another way" + "Can\'t confirm? You’ll need to reset your identity." "Turn off" "You will lose your encrypted messages if you are signed out of all devices." "Are you sure you want to turn off backup?" diff --git a/features/verifysession/impl/src/main/res/values-be/translations.xml b/features/verifysession/impl/src/main/res/values-be/translations.xml index cdb8cedfe0..da894a2ddb 100644 --- a/features/verifysession/impl/src/main/res/values-be/translations.xml +++ b/features/verifysession/impl/src/main/res/values-be/translations.xml @@ -28,4 +28,5 @@ "Яны супадаюць" "Для працягу працы прыміце запыт на запуск працэсу праверкі ў іншым сеансе." "Чаканне прыняцця запыту" + "Выхад…" diff --git a/features/verifysession/impl/src/main/res/values-bg/translations.xml b/features/verifysession/impl/src/main/res/values-bg/translations.xml index 45263503f3..bd4c7071ef 100644 --- a/features/verifysession/impl/src/main/res/values-bg/translations.xml +++ b/features/verifysession/impl/src/main/res/values-bg/translations.xml @@ -14,4 +14,5 @@ "Те съвпадат" "Приемете заявката, за да започнете процеса на потвърждаване в другата си сесия, за да продължите." "В очакване на приемане на заявка" + "Излизане…" diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml index 7d012a09da..bfa46cafd9 100644 --- a/features/verifysession/impl/src/main/res/values-cs/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -28,4 +28,5 @@ "Shodují se" "Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci." "Čekání na přijetí žádosti" + "Odhlašování…" 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 a66479b3c1..5f0d9504f4 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -26,4 +26,5 @@ "Sie stimmen überein" "Akzeptiere die Anfrage, um den Verifizierungsprozess in deiner anderen Session zu starten, um fortzufahren." "Warten auf die Annahme der Anfrage" + "Abmelden…" diff --git a/features/verifysession/impl/src/main/res/values-el/translations.xml b/features/verifysession/impl/src/main/res/values-el/translations.xml index 93838717b7..cc99091441 100644 --- a/features/verifysession/impl/src/main/res/values-el/translations.xml +++ b/features/verifysession/impl/src/main/res/values-el/translations.xml @@ -26,4 +26,5 @@ "Ταιριάζουν" "Αποδέξου το αίτημα για να ξεκινήσεις τη διαδικασία επαλήθευσης στην άλλη συνεδρία σου για να συνεχίσεις." "Αναμονή για αποδοχή αιτήματος" + "Αποσύνδεση…" diff --git a/features/verifysession/impl/src/main/res/values-es/translations.xml b/features/verifysession/impl/src/main/res/values-es/translations.xml index 75773bd081..9c43fe28e8 100644 --- a/features/verifysession/impl/src/main/res/values-es/translations.xml +++ b/features/verifysession/impl/src/main/res/values-es/translations.xml @@ -24,4 +24,5 @@ "Coinciden" "Acepta la solicitud para iniciar el proceso de verificación en tu otra sesión para continuar." "A la espera de aceptar la solicitud" + "Cerrando sesión…" 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 a9baa5bd9e..855bcc950b 100644 --- a/features/verifysession/impl/src/main/res/values-et/translations.xml +++ b/features/verifysession/impl/src/main/res/values-et/translations.xml @@ -28,4 +28,5 @@ "Nad klapivad omavahel" "Jätkamaks nõustu verifitseerimisprotsessi alustamisega oma teises sessioonis." "Ootame nõustumist verifitseerimispäringuga" + "Logime välja…" diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml index b1f3ad365b..d80520199a 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -28,4 +28,5 @@ "Ils correspondent" "Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session." "En attente d’acceptation de la demande" + "Déconnexion…" diff --git a/features/verifysession/impl/src/main/res/values-hu/translations.xml b/features/verifysession/impl/src/main/res/values-hu/translations.xml index 3de5d84c48..52c92e16f6 100644 --- a/features/verifysession/impl/src/main/res/values-hu/translations.xml +++ b/features/verifysession/impl/src/main/res/values-hu/translations.xml @@ -28,4 +28,5 @@ "Megegyeznek" "A folytatáshoz fogadja el az ellenőrzési folyamat indítási kérését a másik munkamenetében." "Várakozás a kérés elfogadására" + "Kijelentkezés…" 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 50dc87d765..8fe930f24f 100644 --- a/features/verifysession/impl/src/main/res/values-in/translations.xml +++ b/features/verifysession/impl/src/main/res/values-in/translations.xml @@ -26,4 +26,5 @@ "Mereka cocok" "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan." "Menunggu untuk menerima permintaan" + "Mengeluarkan dari akun…" diff --git a/features/verifysession/impl/src/main/res/values-it/translations.xml b/features/verifysession/impl/src/main/res/values-it/translations.xml index d36733d08b..c1154a8c89 100644 --- a/features/verifysession/impl/src/main/res/values-it/translations.xml +++ b/features/verifysession/impl/src/main/res/values-it/translations.xml @@ -28,4 +28,5 @@ "Corrispondono" "Accetta la richiesta di avviare il processo di verifica nell\'altra sessione per continuare." "In attesa di accettare la richiesta" + "Disconnessione in corso…" diff --git a/features/verifysession/impl/src/main/res/values-ka/translations.xml b/features/verifysession/impl/src/main/res/values-ka/translations.xml index afd1b03a56..89f1a068ea 100644 --- a/features/verifysession/impl/src/main/res/values-ka/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ka/translations.xml @@ -17,4 +17,5 @@ "ისინი ემთხვევიან ერთმანეთს" "მიიღეთ დადასტურების მოთხოვნა თქვენს სხვა სესიაში ამ პროცესის გასაგრძელებლად." "მოთხოვნის მიღებას ველოდებით" + "გასვლა…" diff --git a/features/verifysession/impl/src/main/res/values-nl/translations.xml b/features/verifysession/impl/src/main/res/values-nl/translations.xml index b04e03307d..6330ad2fc6 100644 --- a/features/verifysession/impl/src/main/res/values-nl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nl/translations.xml @@ -18,4 +18,5 @@ "Ze komen overeen" "Accepteer het verzoek tot verificatie in je andere sessie om door te gaan." "Wachten om verzoek te accepteren" + "Uitloggen…" 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 506889fbae..41d950f2f1 100644 --- a/features/verifysession/impl/src/main/res/values-pl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pl/translations.xml @@ -28,4 +28,5 @@ "Pasują do siebie" "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować." "Oczekiwanie na zaakceptowanie żądania" + "Wylogowywanie…" diff --git a/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml b/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml index 31da2e43d1..9874cfa2b3 100644 --- a/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt-rBR/translations.xml @@ -21,4 +21,5 @@ "Eles combinam" "Aceite a solicitação para iniciar o processo de verificação em sua outra sessão para continuar." "Aguardando para aceitar a solicitação" + "Saindo…" 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 a4d45ec9ef..0c7731faf9 100644 --- a/features/verifysession/impl/src/main/res/values-pt/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pt/translations.xml @@ -28,4 +28,5 @@ "Correspondem" "Para continuar, aceita o pedido de verificação na tua outra sessão." "À aguardar a aceitação do pedido" + "A terminar sessão…" 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 69bb6a20f5..d22bb0c435 100644 --- a/features/verifysession/impl/src/main/res/values-ro/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ro/translations.xml @@ -26,4 +26,5 @@ "Se potrivesc" "Acceptați solicitarea de a începe procesul de verificare în cealaltă sesiune pentru a continua." "Se așteptă acceptarea cererii" + "Deconectare în curs…" diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index fc685356d7..d92b5c6f7b 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -34,4 +34,5 @@ "Они совпадают" "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе." "Ожидание принятия запроса" + "Выполняется выход…" 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 a2ec2cb4b2..8d32f2d362 100644 --- a/features/verifysession/impl/src/main/res/values-sk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml @@ -28,4 +28,5 @@ "Zhodujú sa" "Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii." "Čaká sa na prijatie žiadosti" + "Prebieha odhlasovanie…" diff --git a/features/verifysession/impl/src/main/res/values-sv/translations.xml b/features/verifysession/impl/src/main/res/values-sv/translations.xml index 524bc02b1e..ca87481bc9 100644 --- a/features/verifysession/impl/src/main/res/values-sv/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sv/translations.xml @@ -28,4 +28,5 @@ "De matchar" "Godkänn begäran om att starta verifieringsprocessen på din andra session för att fortsätta." "Väntar på att acceptera begäran" + "Loggar ut …" 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 b76304f537..3a2b3cea6b 100644 --- a/features/verifysession/impl/src/main/res/values-uk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uk/translations.xml @@ -25,4 +25,5 @@ "Вони збігаються" "Щоб продовжити, прийміть запит на початок процесу перевірки в іншому сеансі." "Очікування на прийняття запиту" + "Вихід…" diff --git a/features/verifysession/impl/src/main/res/values-uz/translations.xml b/features/verifysession/impl/src/main/res/values-uz/translations.xml index 87b780aa1a..45596e1651 100644 --- a/features/verifysession/impl/src/main/res/values-uz/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uz/translations.xml @@ -14,4 +14,5 @@ "Ular mos keladi" "Davom etish uchun boshqa seansda tekshirish jarayonini boshlash soʻrovini qabul qiling." "Soʻrovni qabul qilish kutilmoqda" + "Chiqish…" diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml index 512e806c47..87e3e69de3 100644 --- a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml @@ -23,4 +23,5 @@ "一樣" "準備開始驗證,請到您的其他工作階段接受請求。" "等待接受請求" + "正在登出…" 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 765da43569..5d8e7bb200 100644 --- a/features/verifysession/impl/src/main/res/values-zh/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh/translations.xml @@ -28,4 +28,5 @@ "匹配" "请在其他会话中接受验证请求。" "等待接受请求" + "正在登出…" diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml index 6ed3f97dbe..e1e482eec9 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -34,6 +34,7 @@ "Réponse rapide" "Vous a invité(e) à rejoindre le salon" "Moi" + "%1$s mentionné ou en réponse" "Vous êtes en train de voir la notification ! Cliquez-moi !" "%1$s : %2$s" "%1$s : %2$s %3$s" diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml index 30a2128d63..c1472ebaeb 100644 --- a/libraries/push/impl/src/main/res/values-ru/translations.xml +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -38,6 +38,7 @@ "Быстрый ответ" "Пригласил вас в комнату" "Я" + "%1$s упомянул или ответил" "Вы просматриваете уведомление! Нажмите на меня!" "%1$s: %2$s" "%1$s: %2$s %3$s" diff --git a/libraries/push/impl/src/main/res/values-sv/translations.xml b/libraries/push/impl/src/main/res/values-sv/translations.xml index 92036e09c2..620263c8fc 100644 --- a/libraries/push/impl/src/main/res/values-sv/translations.xml +++ b/libraries/push/impl/src/main/res/values-sv/translations.xml @@ -34,6 +34,7 @@ "Snabbsvar" "Bjöd in dig att gå med i rummet" "Jag" + "%1$s nämnde eller svarade" "Du tittar på aviseringen! Klicka på mig!" "%1$s: %2$s" "%1$s: %2$s %3$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 0e11f3f37e..258f386137 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -261,6 +261,7 @@ Raison: %1$s." "Certains messages n’ont pas été envoyés" "Désolé, une erreur s’est produite" "L’authenticité de ce message chiffré ne peut être garantie sur cet appareil." + "Chiffré par un utilisateur précédemment vérifié." "Non chiffré." "Chiffré par un appareil inconnu ou supprimé." "Chiffré par un appareil non vérifié par son propriétaire." 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 16c2973b08..6dd12d85df 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -268,6 +268,7 @@ "Некоторые сообщения не были отправлены" "Извините, произошла ошибка" "Подлинность этого зашифрованного сообщения не может быть гарантирована на этом устройстве." + "Зашифровано ранее проверенным пользователем." "Не зашифровано." "Зашифровано неизвестным или удаленным устройством." "Зашифровано устройством, не проверенным его владельцем." @@ -287,6 +288,8 @@ "%1$d Закрепленных сообщений" "Закрепленные сообщения" + "Вы собираетесь перейти в свою учетную запись %1$s, чтобы сбросить идентификацию. После этого вы вернетесь в приложение." + "Не можете подтвердить? Перейдите в свою учетную запись, чтобы сбросить свою идентификацию." "Закрепленные сообщения" "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось получить данные о пользователе" 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 ee333c84b2..9a73ec274f 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -262,6 +262,7 @@ Anledning:%1$s." "Vissa meddelanden har inte skickats" "Tyvärr, ett fel uppstod" "Detta krypterade meddelandes äkthet kan inte garanteras på den här enheten." + "Krypterat av en tidigare verifierad användare." "Inte krypterad." "Krypterad av en okänd eller raderad enhet." "Krypterad av en enhet som inte verifierats av ägaren." @@ -280,6 +281,8 @@ Anledning:%1$s." "%1$d Fästa meddelanden" "Fästa meddelanden" + "Du är på väg att gå till ditt %1$s-konto för att återställa din identitet. Därefter kommer du att tas tillbaka till appen." + "Kan du inte bekräfta? Gå till ditt konto för att återställa din identitet." "Fästa meddelanden" "Misslyckades att bearbeta media för uppladdning, vänligen pröva igen." "Kunde inte hämta användarinformation" 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 a80883710c..79571dd613 100644 --- a/libraries/ui-strings/src/main/res/values-uz/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uz/translations.xml @@ -43,8 +43,8 @@ "Takliflar" "Qo\'shilish" "Batafsil malumot" - "Tark etish " - "Xonani tark etish " + "Tark etish" + "Xonani tark etish" "Hisobni boshqarish" "Qurilmalarni boshqarish" "Keyingisi" @@ -78,7 +78,7 @@ "Tasdiqlashni boshlang" "Xaritani yuklash uchun bosing" "Rasmga olmoq" - "Manbani korish " + "Manbani korish" "Ha" "Haqida" "Qabul qilinadigan foydalanish siyosati" diff --git a/screenshots/html/data.js b/screenshots/html/data.js index cf40a7bf10..a726187aef 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,42 +1,42 @@ // 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",19958,], -["features.preferences.impl.about_AboutView_Day_1_en","features.preferences.impl.about_AboutView_Night_1_en",19958,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",19965,], +["features.preferences.impl.about_AboutView_Day_1_en","features.preferences.impl.about_AboutView_Night_1_en",19965,], ["features.invite.impl.response_AcceptDeclineInviteView_Day_0_en","features.invite.impl.response_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",19958,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",19958,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",19958,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",19958,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",19965,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",19965,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",19965,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",19965,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_4_en","features.login.impl.accountprovider_AccountProviderView_Night_4_en",0,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",19958,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",19958,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",19958,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",19958,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",19958,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",19958,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",19958,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",19958,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",19958,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",19958,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",19958,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",19958,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",19958,], -["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",19958,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",19965,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",19965,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",19965,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",19965,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",19965,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",19965,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",19965,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",19965,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",19965,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",19965,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",19965,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",19965,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",19965,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",19965,], ["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",19958,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",19965,], ["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",19958,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",19965,], ["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",19958,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",19965,], ["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",19958,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",19965,], ["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,], @@ -46,11 +46,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.attachments.preview_AttachmentsView_0_en","",19958,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",19958,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",19958,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",19958,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",19958,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",19965,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",19965,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",19965,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",19965,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",19965,], ["libraries.designsystem.components.avatar_Avatar_Avatars_0_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_10_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_11_en","",0,], @@ -130,13 +130,13 @@ export const screenshots = [ ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], ["libraries.designsystem.components_BigCheckmark_Day_0_en","libraries.designsystem.components_BigCheckmark_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",19958,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",19958,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",19965,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",19965,], ["libraries.designsystem.components_BloomInitials_Day_0_en","libraries.designsystem.components_BloomInitials_Night_0_en",0,], ["libraries.designsystem.components_BloomInitials_Day_1_en","libraries.designsystem.components_BloomInitials_Night_1_en",0,], ["libraries.designsystem.components_BloomInitials_Day_2_en","libraries.designsystem.components_BloomInitials_Night_2_en",0,], @@ -147,84 +147,83 @@ export const screenshots = [ ["libraries.designsystem.components_BloomInitials_Day_7_en","libraries.designsystem.components_BloomInitials_Night_7_en",0,], ["libraries.designsystem.components_Bloom_Day_0_en","libraries.designsystem.components_Bloom_Night_0_en",0,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",19958,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",19958,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",19958,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",19958,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",19958,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",19965,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",19965,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",19965,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",19965,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",19965,], ["libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonColumnMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_ButtonRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ButtonRowMolecule_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_0_en","features.call.impl.ui_CallScreenPipView_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_1_en","features.call.impl.ui_CallScreenPipView_Night_1_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",19958,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",19958,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",19958,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",19958,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",19958,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",19965,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",19965,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",19965,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",19965,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",19965,], ["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",19958,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",19958,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",19965,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",19965,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",19958,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",19965,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], ["libraries.textcomposer.components_ComposerOptionsButton_Day_0_en","libraries.textcomposer.components_ComposerOptionsButton_Night_0_en",0,], ["libraries.designsystem.components.avatar_CompositeAvatar_Avatars_en","",0,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",19958,], -["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",19958,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_0_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_0_en",19965,], +["features.createroom.impl.configureroom_ConfigureRoomView_Day_1_en","features.createroom.impl.configureroom_ConfigureRoomView_Night_1_en",19965,], ["features.preferences.impl.developer.tracing_ConfigureTracingView_Day_0_en","features.preferences.impl.developer.tracing_ConfigureTracingView_Night_0_en",0,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",19958,], -["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",19958,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",19965,], +["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",19965,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicatorView_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicatorView_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",19958,], -["features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Day_0_en","features.securebackup.impl.createkey_CreateNewRecoveryKeyView_Night_0_en",19958,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",19958,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",19958,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",19958,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",19958,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",19958,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",19958,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",19958,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",19958,], -["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",19958,], -["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",19958,], -["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",19958,], -["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",19958,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",19958,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",19958,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",19965,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",19965,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",19965,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",19965,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",19965,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",19965,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",19965,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",19965,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",19965,], +["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",19965,], +["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",19965,], +["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",19965,], +["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",19965,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime pickers_en","",19965,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime pickers_en","",19965,], ["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",19958,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",19958,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",19958,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",19965,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",19965,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",19965,], ["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",19958,], -["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",19958,], -["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",19958,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",19958,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",19958,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",19958,], -["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",19958,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",19965,], +["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",19965,], +["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",19965,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",19965,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",19965,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",19965,], +["libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Day_0_en","libraries.designsystem.atomic.molecules_DialogLikeBannerMolecule_Night_0_en",19965,], ["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,], @@ -236,12 +235,12 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",19958,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",19958,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",19958,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",19958,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",19958,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",19958,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",19965,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",19965,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",19965,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",19965,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",19965,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",19965,], ["libraries.matrix.ui.components_EditableAvatarView_Day_0_en","libraries.matrix.ui.components_EditableAvatarView_Night_0_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_1_en","libraries.matrix.ui.components_EditableAvatarView_Night_1_en",0,], ["libraries.matrix.ui.components_EditableAvatarView_Day_2_en","libraries.matrix.ui.components_EditableAvatarView_Night_2_en",0,], @@ -251,10 +250,10 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiItem_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiItem_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en",0,], -["features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_en","features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Night_0_en",19958,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",19958,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",19958,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",19958,], +["features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Day_0_en","features.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_Night_0_en",19965,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",19965,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",19965,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",19965,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], ["libraries.designsystem.theme.components_FilledButtonLargeLowPadding_Buttons_en","",0,], @@ -265,15 +264,15 @@ export const screenshots = [ ["libraries.designsystem.theme.components_FloatingActionButton_Floating Action Buttons_en","",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",19958,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",19958,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",19958,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",19965,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",19965,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",19965,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_0_en","features.messages.impl.forward_ForwardMessagesView_Night_0_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_1_en","features.messages.impl.forward_ForwardMessagesView_Night_1_en",0,], ["features.messages.impl.forward_ForwardMessagesView_Day_2_en","features.messages.impl.forward_ForwardMessagesView_Night_2_en",0,], -["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",19958,], -["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",19958,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",19965,], +["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",19965,], ["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,], @@ -300,37 +299,37 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",19958,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",19965,], ["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",19958,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",19965,], ["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",19958,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",19965,], ["libraries.designsystem.atomic.molecules_InfoListItemMolecule_Day_0_en","libraries.designsystem.atomic.molecules_InfoListItemMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.organisms_InfoListOrganism_Day_0_en","libraries.designsystem.atomic.organisms_InfoListOrganism_Night_0_en",0,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",19958,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",19965,], ["features.joinroom.impl_JoinRoomView_Day_0_en","features.joinroom.impl_JoinRoomView_Night_0_en",0,], ["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",0,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",19958,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",19958,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",19965,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",19965,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], ["libraries.designsystem.components_LabelledOutlinedTextField_Day_0_en","libraries.designsystem.components_LabelledOutlinedTextField_Night_0_en",0,], ["libraries.designsystem.components_LabelledTextField_Day_0_en","libraries.designsystem.components_LabelledTextField_Night_0_en",0,], ["features.leaveroom.api_LeaveRoomView_Day_0_en","features.leaveroom.api_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",19958,], -["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",19958,], -["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",19958,], -["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",19958,], -["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",19958,], -["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",19958,], +["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",19965,], +["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",19965,], +["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",19965,], +["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",19965,], +["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",19965,], +["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",19965,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress Indicators_en","",0,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], @@ -381,28 +380,28 @@ 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",19958,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",19958,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",19958,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",19958,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",19965,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",19965,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",19965,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",19965,], ["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",19958,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",19958,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",19958,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",19958,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",19958,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",19958,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",19958,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",19958,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",19958,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",19958,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",19958,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",19958,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",19958,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",19958,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",19958,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",19965,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",19965,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",19965,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",19965,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",19965,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",19965,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",19965,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",19965,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",19965,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",19965,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",19965,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",19965,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",19965,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",19965,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",19965,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",19958,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",19965,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Day_0_en","libraries.matrix.ui.components_MatrixUserHeaderPlaceholder_Night_0_en",0,], ["libraries.matrix.ui.components_MatrixUserHeader_Day_0_en","libraries.matrix.ui.components_MatrixUserHeader_Night_0_en",0,], @@ -412,7 +411,7 @@ export const screenshots = [ ["libraries.mediaviewer.api.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_10_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",19958,], +["libraries.mediaviewer.api.viewer_MediaViewerView_2_en","",19965,], ["libraries.mediaviewer.api.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.api.viewer_MediaViewerView_5_en","",0,], @@ -424,7 +423,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",19958,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",19965,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_10_en","features.messages.impl.timeline.components_MessageEventBubble_Night_10_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_11_en","features.messages.impl.timeline.components_MessageEventBubble_Night_11_en",0,], @@ -441,7 +440,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_8_en","features.messages.impl.timeline.components_MessageEventBubble_Night_8_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_9_en","features.messages.impl.timeline.components_MessageEventBubble_Night_9_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",19958,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",19965,], ["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,], @@ -449,26 +448,26 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_1_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_1_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_0_en","features.messages.impl.typing_MessagesViewWithTyping_Night_0_en",19958,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_1_en","features.messages.impl.typing_MessagesViewWithTyping_Night_1_en",19958,], -["features.messages.impl.typing_MessagesViewWithTyping_Day_2_en","features.messages.impl.typing_MessagesViewWithTyping_Night_2_en",19958,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",19958,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",19958,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",19958,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",19958,], -["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",19958,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",19958,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",19958,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",19958,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",19958,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",19958,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",19958,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",19958,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",19958,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",19958,], -["features.roomlist.impl.migration_MigrationScreenView_Day_0_en","features.roomlist.impl.migration_MigrationScreenView_Night_0_en",19958,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_0_en","features.messages.impl.typing_MessagesViewWithTyping_Night_0_en",19965,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_1_en","features.messages.impl.typing_MessagesViewWithTyping_Night_1_en",19965,], +["features.messages.impl.typing_MessagesViewWithTyping_Day_2_en","features.messages.impl.typing_MessagesViewWithTyping_Night_2_en",19965,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",19965,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",19965,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",19965,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",19965,], +["features.messages.impl_MessagesView_Day_13_en","features.messages.impl_MessagesView_Night_13_en",19965,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",19965,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",19965,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",19965,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",19965,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",19965,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",19965,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",19965,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",19965,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",19965,], +["features.roomlist.impl.migration_MigrationScreenView_Day_0_en","features.roomlist.impl.migration_MigrationScreenView_Night_0_en",19965,], ["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",19958,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",19965,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -477,28 +476,28 @@ export const screenshots = [ ["libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple selection List item - selection in trailing content_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple selection List item - selection in supporting text_List items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple selection List item - no selection_List items_en","",0,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",19958,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",19958,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",19958,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",19965,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",19965,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",19965,], ["libraries.oidc.impl.webview_OidcView_Day_0_en","libraries.oidc.impl.webview_OidcView_Night_0_en",0,], ["libraries.oidc.impl.webview_OidcView_Day_1_en","libraries.oidc.impl.webview_OidcView_Night_1_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",19958,], -["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",19958,], -["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",19958,], -["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",19958,], -["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",19958,], +["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",19965,], +["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",19965,], +["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",19965,], +["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",19965,], +["features.onboarding.impl_OnBoardingView_Day_4_en","features.onboarding.impl_OnBoardingView_Night_4_en",19965,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], @@ -513,57 +512,57 @@ export const screenshots = [ ["libraries.designsystem.components_PageTitleWithIconFull_Day_3_en","libraries.designsystem.components_PageTitleWithIconFull_Night_3_en",0,], ["libraries.designsystem.components_PageTitleWithIconFull_Day_4_en","libraries.designsystem.components_PageTitleWithIconFull_Night_4_en",0,], ["libraries.designsystem.components_PageTitleWithIconMinimal_Day_0_en","libraries.designsystem.components_PageTitleWithIconMinimal_Night_0_en",0,], -["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",19958,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",19958,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",19958,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",19958,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",19958,], +["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",19965,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",19965,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",19965,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",19965,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",19965,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["libraries.designsystem.components_PinIcon_Day_0_en","libraries.designsystem.components_PinIcon_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",19958,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",19958,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",19965,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",19965,], ["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_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",19958,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",19958,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",19965,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",19965,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",19958,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",19958,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",19958,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",19958,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",19958,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",19965,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",19965,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",19965,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",19965,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",19965,], ["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",19958,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",19958,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",19958,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",19958,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",19958,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",19958,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",19958,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",19958,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",19958,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",19958,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",19958,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",19965,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",19965,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",19965,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",19965,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",19965,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",19965,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",19965,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",19965,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",19965,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",19965,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",19965,], ["libraries.designsystem.components.preferences_PreferenceCategory_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceCheckbox_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceDivider_Preferences_en","",0,], @@ -579,188 +578,189 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceTextLight_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",19958,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",19958,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",19958,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",19958,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",19965,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",19965,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",19965,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",19965,], ["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","",19958,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",19958,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",19958,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",19958,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",19958,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",19958,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",19958,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",19958,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",19958,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",19958,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",19958,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",19958,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",19965,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",19965,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",19965,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",19965,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",19965,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",19965,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",19965,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",19965,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",19965,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",19965,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",19965,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",19965,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",19958,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",19958,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",19965,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",19965,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",19958,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",19958,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",19958,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",19958,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",19958,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",19958,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",19958,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",19965,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",19965,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",19965,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",19965,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",19965,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",19965,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",19965,], ["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",19958,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",19958,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",19958,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",19958,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",19958,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",19958,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",19958,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",19958,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",19958,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",19958,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",19965,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",19965,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",19965,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",19965,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",19965,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",19965,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",19965,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",19965,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",19965,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",19965,], ["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",0,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",19958,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",19958,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",19958,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",19958,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",19965,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",19965,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",19965,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",19965,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",19958,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",19965,], ["features.roomdetails.impl.components_RoomBadgeNegative_Day_0_en","features.roomdetails.impl.components_RoomBadgeNegative_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgeNeutral_Day_0_en","features.roomdetails.impl.components_RoomBadgeNeutral_Night_0_en",0,], ["features.roomdetails.impl.components_RoomBadgePositive_Day_0_en","features.roomdetails.impl.components_RoomBadgePositive_Night_0_en",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",19958,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",19958,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",19958,], -["features.roomdetails.impl_RoomDetails_0_en","",19958,], -["features.roomdetails.impl_RoomDetails_10_en","",19958,], -["features.roomdetails.impl_RoomDetails_11_en","",19958,], -["features.roomdetails.impl_RoomDetails_12_en","",19958,], -["features.roomdetails.impl_RoomDetails_1_en","",19958,], -["features.roomdetails.impl_RoomDetails_2_en","",19958,], -["features.roomdetails.impl_RoomDetails_3_en","",19958,], -["features.roomdetails.impl_RoomDetails_4_en","",19958,], -["features.roomdetails.impl_RoomDetails_5_en","",19958,], -["features.roomdetails.impl_RoomDetails_6_en","",19958,], -["features.roomdetails.impl_RoomDetails_7_en","",19958,], -["features.roomdetails.impl_RoomDetails_8_en","",19958,], -["features.roomdetails.impl_RoomDetails_9_en","",19958,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",19958,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",19958,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",19958,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",19958,], -["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",19958,], -["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",19958,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",19965,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",19965,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",19965,], +["features.roomdetails.impl_RoomDetails_0_en","",19965,], +["features.roomdetails.impl_RoomDetails_10_en","",19965,], +["features.roomdetails.impl_RoomDetails_11_en","",19965,], +["features.roomdetails.impl_RoomDetails_12_en","",19965,], +["features.roomdetails.impl_RoomDetails_1_en","",19965,], +["features.roomdetails.impl_RoomDetails_2_en","",19965,], +["features.roomdetails.impl_RoomDetails_3_en","",19965,], +["features.roomdetails.impl_RoomDetails_4_en","",19965,], +["features.roomdetails.impl_RoomDetails_5_en","",19965,], +["features.roomdetails.impl_RoomDetails_6_en","",19965,], +["features.roomdetails.impl_RoomDetails_7_en","",19965,], +["features.roomdetails.impl_RoomDetails_8_en","",19965,], +["features.roomdetails.impl_RoomDetails_9_en","",19965,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",19965,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",19965,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",19965,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",19965,], +["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",19965,], +["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",19965,], ["features.roomlist.impl.components_RoomListContentView_Day_2_en","features.roomlist.impl.components_RoomListContentView_Night_2_en",0,], -["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",19958,], -["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",19958,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",19958,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",19958,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",19958,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",19958,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",19958,], +["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",19965,], +["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",19965,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",19965,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",19965,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",19965,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",19965,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",19965,], ["features.roomlist.impl.search_RoomListSearchContent_Day_0_en","features.roomlist.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",19958,], -["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",19958,], -["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",19958,], +["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",19965,], +["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",19965,], +["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",19965,], ["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",0,], -["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",19958,], -["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",19958,], -["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",19958,], -["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",19958,], -["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",19958,], -["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",19958,], -["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",19958,], +["features.roomlist.impl_RoomListView_Day_11_en","features.roomlist.impl_RoomListView_Night_11_en",0,], +["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",19965,], +["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",19965,], +["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",19965,], +["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",19965,], +["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",19965,], +["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",19965,], +["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",19965,], ["features.roomlist.impl_RoomListView_Day_8_en","features.roomlist.impl_RoomListView_Night_8_en",0,], -["features.roomlist.impl_RoomListView_Day_9_en","features.roomlist.impl_RoomListView_Night_9_en",19958,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",19958,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",19958,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",19958,], +["features.roomlist.impl_RoomListView_Day_9_en","features.roomlist.impl_RoomListView_Night_9_en",19965,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",19965,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",19965,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",19965,], ["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",0,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",19958,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",19958,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",19965,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",19965,], ["libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Day_0_en","libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Night_0_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",19958,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",19958,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",19965,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",19965,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_9_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",19958,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",19958,], -["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",19958,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",19958,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",19965,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",19965,], +["features.createroom.impl.components_RoomPrivacyOption_Day_0_en","features.createroom.impl.components_RoomPrivacyOption_Night_0_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",19965,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",19965,], ["features.roomlist.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.roomlist.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_0_en","features.roomlist.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_10_en","features.roomlist.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -783,10 +783,10 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_26_en","features.roomlist.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_27_en","features.roomlist.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_28_en","features.roomlist.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",19958,], -["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",19958,], -["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",19958,], -["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",19958,], +["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",19965,], +["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",19965,], +["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",19965,], +["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",19965,], ["features.roomlist.impl.components_RoomSummaryRow_Day_3_en","features.roomlist.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_4_en","features.roomlist.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_5_en","features.roomlist.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -794,64 +794,64 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_7_en","features.roomlist.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_8_en","features.roomlist.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_9_en","features.roomlist.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",19958,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",19958,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",19958,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",19965,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",19965,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",19965,], ["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",19958,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",19958,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",19958,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",19965,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",19965,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",19965,], ["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","",19958,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search views_en","",19965,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search views_en","",0,], -["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",19958,], -["features.createroom.impl.components_SearchSingleUserResultItem_en","",19958,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",19958,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",19958,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",19958,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",19958,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",19958,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",19958,], -["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",19958,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",19958,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",19958,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",19958,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",19958,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",19958,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",19958,], +["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",19965,], +["features.createroom.impl.components_SearchSingleUserResultItem_en","",19965,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",19965,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",19965,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",19965,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",19965,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_0_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_0_en",19965,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_1_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_1_en",19965,], +["features.securebackup.impl.enable_SecureBackupEnableView_Day_2_en","features.securebackup.impl.enable_SecureBackupEnableView_Night_2_en",19965,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",19965,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",19965,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",19965,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",19965,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",19965,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",19965,], ["libraries.matrix.ui.components_SelectedRoom_Day_0_en","libraries.matrix.ui.components_SelectedRoom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_1_en","libraries.matrix.ui.components_SelectedRoom_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedUserCannotRemove_Day_0_en","libraries.matrix.ui.components_SelectedUserCannotRemove_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedUser_Day_0_en","libraries.matrix.ui.components_SelectedUser_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButton_Day_0_en","libraries.textcomposer.components_SendButton_Night_0_en",0,], -["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",19958,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",19958,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",19958,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",19958,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",19958,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",19965,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",19965,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",19965,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",19965,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",19965,], ["libraries.matrix.ui.messages.sender_SenderName_Day_0_en","libraries.matrix.ui.messages.sender_SenderName_Night_0_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_1_en","libraries.matrix.ui.messages.sender_SenderName_Night_1_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_2_en","libraries.matrix.ui.messages.sender_SenderName_Night_2_en",0,], @@ -861,38 +861,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.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",19958,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",19958,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",19958,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",19958,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",19958,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",19958,], +["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",0,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",19965,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",19965,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",19965,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",19965,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",19965,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",19965,], ["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",19958,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",19965,], ["features.messages.impl.timeline.components.reactionsummary_SheetContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_SheetContent_Night_0_en",0,], ["features.messages.impl.actionlist_SheetContent_Day_0_en","features.messages.impl.actionlist_SheetContent_Night_0_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_10_en","features.messages.impl.actionlist_SheetContent_Night_10_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_11_en","features.messages.impl.actionlist_SheetContent_Night_11_en",19958,], +["features.messages.impl.actionlist_SheetContent_Day_10_en","features.messages.impl.actionlist_SheetContent_Night_10_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_11_en","features.messages.impl.actionlist_SheetContent_Night_11_en",19965,], ["features.messages.impl.actionlist_SheetContent_Day_1_en","features.messages.impl.actionlist_SheetContent_Night_1_en",0,], -["features.messages.impl.actionlist_SheetContent_Day_2_en","features.messages.impl.actionlist_SheetContent_Night_2_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_3_en","features.messages.impl.actionlist_SheetContent_Night_3_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_4_en","features.messages.impl.actionlist_SheetContent_Night_4_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_5_en","features.messages.impl.actionlist_SheetContent_Night_5_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_6_en","features.messages.impl.actionlist_SheetContent_Night_6_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_7_en","features.messages.impl.actionlist_SheetContent_Night_7_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_8_en","features.messages.impl.actionlist_SheetContent_Night_8_en",19958,], -["features.messages.impl.actionlist_SheetContent_Day_9_en","features.messages.impl.actionlist_SheetContent_Night_9_en",19958,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",19958,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",19958,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",19958,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",19958,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",19958,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",19958,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",19958,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",19958,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",19958,], +["features.messages.impl.actionlist_SheetContent_Day_2_en","features.messages.impl.actionlist_SheetContent_Night_2_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_3_en","features.messages.impl.actionlist_SheetContent_Night_3_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_4_en","features.messages.impl.actionlist_SheetContent_Night_4_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_5_en","features.messages.impl.actionlist_SheetContent_Night_5_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_6_en","features.messages.impl.actionlist_SheetContent_Night_6_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_7_en","features.messages.impl.actionlist_SheetContent_Night_7_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_8_en","features.messages.impl.actionlist_SheetContent_Night_8_en",19965,], +["features.messages.impl.actionlist_SheetContent_Day_9_en","features.messages.impl.actionlist_SheetContent_Night_9_en",19965,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",19965,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",19965,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",19965,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",19965,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",19965,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",19965,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",19965,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",19965,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",19965,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_SingleSelectionListItemCustomFormattert_Single selection List item - custom formatter_List items_en","",0,], @@ -901,7 +902,7 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItemUnselectedWithSupportingText_Single selection List item - no selection, supporting text_List items_en","",0,], ["libraries.designsystem.components.list_SingleSelectionListItem_Single selection List item - no selection_List items_en","",0,], ["libraries.designsystem.theme.components_Sliders_Sliders_en","",0,], -["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",19958,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",19965,], ["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,], @@ -911,37 +912,37 @@ export const screenshots = [ ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], ["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",0,], -["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",19958,], +["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",19965,], ["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",19961,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",19965,], ["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",19958,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",19965,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",19958,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",19958,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",19958,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",19958,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",19958,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",19958,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",19958,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",19965,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",19965,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",19965,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",19965,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",19965,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",19965,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",19965,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], ["libraries.designsystem.theme.components_TextFieldDark_TextFields_en","",0,], @@ -953,26 +954,26 @@ export const screenshots = [ ["libraries.designsystem.theme.components_TextFieldValueTextFieldDark_TextFields_en","",0,], ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",19958,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",19958,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",19958,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime pickers_en","",19965,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime pickers_en","",19965,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime pickers_en","",19965,], ["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",19958,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",19958,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",19965,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",19965,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",19958,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",19965,], ["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",19958,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",19958,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",19958,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",19965,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",19965,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",19965,], ["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,], @@ -980,16 +981,16 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",19958,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",19958,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",19965,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",19965,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",19958,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",19965,], ["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",19958,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",19958,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",19965,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",19965,], ["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,], @@ -998,36 +999,36 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",19958,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",19965,], ["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",19958,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",19965,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",19958,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",19965,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",19958,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",19958,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",19965,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",19965,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",19958,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",19965,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",19958,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",19958,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",19958,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",19958,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",19958,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",19965,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",19965,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",19965,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",19965,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",19965,], ["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",19958,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",19958,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",19965,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",19965,], ["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",19958,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",19965,], ["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,], @@ -1036,8 +1037,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",19958,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",19958,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",19965,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",19965,], ["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,], @@ -1049,7 +1050,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",19958,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",19965,], ["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,], @@ -1071,80 +1072,81 @@ 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",19958,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",19958,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",19965,], ["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",0,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",19958,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",19958,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",19965,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",19965,], ["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",19958,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",19965,], ["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",19958,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",19965,], ["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",19958,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",19965,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.theme.components_TopAppBar_App Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",19958,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",19958,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",19965,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",19965,], ["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",19958,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",19958,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",19958,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",19958,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",19958,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",19958,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",19965,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",19965,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",19965,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",19965,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",19965,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",19965,], ["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","",19958,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",19965,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",19958,], -["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",19958,], -["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",19958,], -["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",19958,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",19965,], +["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",19965,], +["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",19965,], +["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",19965,], ["features.createroom.impl.components_UserListView_Day_3_en","features.createroom.impl.components_UserListView_Night_3_en",0,], ["features.createroom.impl.components_UserListView_Day_4_en","features.createroom.impl.components_UserListView_Night_4_en",0,], ["features.createroom.impl.components_UserListView_Day_5_en","features.createroom.impl.components_UserListView_Night_5_en",0,], ["features.createroom.impl.components_UserListView_Day_6_en","features.createroom.impl.components_UserListView_Night_6_en",0,], -["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",19958,], +["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",19965,], ["features.createroom.impl.components_UserListView_Day_8_en","features.createroom.impl.components_UserListView_Night_8_en",0,], -["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",19958,], +["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",19965,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], ["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",0,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",19958,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",19958,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",19958,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",19958,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",19958,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",19958,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",19958,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",19958,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",19958,], -["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",19958,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",19965,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",19965,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",19965,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",19965,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",19965,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",19965,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",19965,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",19965,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_0_en","features.verifysession.impl_VerifySelfSessionView_Night_0_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_10_en","features.verifysession.impl_VerifySelfSessionView_Night_10_en",0,], +["features.verifysession.impl_VerifySelfSessionView_Day_1_en","features.verifysession.impl_VerifySelfSessionView_Night_1_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_2_en","features.verifysession.impl_VerifySelfSessionView_Night_2_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_3_en","features.verifysession.impl_VerifySelfSessionView_Night_3_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_4_en","features.verifysession.impl_VerifySelfSessionView_Night_4_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_5_en","features.verifysession.impl_VerifySelfSessionView_Night_5_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_6_en","features.verifysession.impl_VerifySelfSessionView_Night_6_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_7_en","features.verifysession.impl_VerifySelfSessionView_Night_7_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_8_en","features.verifysession.impl_VerifySelfSessionView_Night_8_en",19965,], +["features.verifysession.impl_VerifySelfSessionView_Day_9_en","features.verifysession.impl_VerifySelfSessionView_Night_9_en",19965,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], @@ -1158,12 +1160,12 @@ export const screenshots = [ ["libraries.textcomposer.components_VoiceMessageRecorderButton_Day_0_en","libraries.textcomposer.components_VoiceMessageRecorderButton_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessageRecording_Day_0_en","libraries.textcomposer.components_VoiceMessageRecording_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessage_Day_0_en","libraries.textcomposer.components_VoiceMessage_Night_0_en",0,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_0_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_0_en",19958,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_1_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_1_en",19958,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_2_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_2_en",19958,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_3_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_3_en",19958,], -["features.login.impl.screens.waitlistscreen_WaitListView_Day_4_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_4_en",19958,], +["features.login.impl.screens.waitlistscreen_WaitListView_Day_0_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_0_en",19965,], +["features.login.impl.screens.waitlistscreen_WaitListView_Day_1_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_1_en",19965,], +["features.login.impl.screens.waitlistscreen_WaitListView_Day_2_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_2_en",19965,], +["features.login.impl.screens.waitlistscreen_WaitListView_Day_3_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_3_en",19965,], +["features.login.impl.screens.waitlistscreen_WaitListView_Day_4_en","features.login.impl.screens.waitlistscreen_WaitListView_Night_4_en",19965,], ["libraries.designsystem.components.media_WaveformPlaybackView_Day_0_en","libraries.designsystem.components.media_WaveformPlaybackView_Night_0_en",0,], -["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",19958,], +["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",19965,], ["libraries.designsystem.ruler_WithRulers_Day_0_en","libraries.designsystem.ruler_WithRulers_Night_0_en",0,], ]; From 6255a78145d6faed5b7950cf4de99d50b3b20365 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 Sep 2024 11:26:59 +0200 Subject: [PATCH 45/57] App migration: also move the cache files. --- .../impl/migrations/AppMigration06.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt index 6672ae1f8a..721e439ca1 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration06.kt @@ -39,8 +39,22 @@ class AppMigration06 @Inject constructor( if (session.cachePath.isEmpty()) { val sessionFile = File(session.sessionPath) val sessionFolder = sessionFile.name - val cachePath = File(cacheDirectory, sessionFolder).absolutePath - sessionStore.updateData(session.copy(cachePath = cachePath)) + val cachePath = File(cacheDirectory, sessionFolder) + sessionStore.updateData(session.copy(cachePath = cachePath.absolutePath)) + // Move existing cache files + listOf( + "matrix-sdk-event-cache.sqlite3", + "matrix-sdk-event-cache.sqlite3-shm", + "matrix-sdk-event-cache.sqlite3-wal", + ).map { fileName -> + File(sessionFile, fileName) + }.takeIf { files -> + files.all { it.exists() } + }?.forEach { cacheFile -> + val targetFile = File(cachePath, cacheFile.name) + cacheFile.copyTo(targetFile) + cacheFile.delete() + } } } } From f03bd282e7eb623d4ef026443e89fcbfc940bf65 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 Sep 2024 11:29:44 +0200 Subject: [PATCH 46/57] Change the way we do not delete the crypto database. --- .../element/android/libraries/matrix/impl/RustMatrixClient.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 f346100de1..089240c94c 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 @@ -645,9 +645,9 @@ class RustMatrixClient( // Delete the folder and all its content sessionPaths.fileDirectory.deleteRecursively() } else { - // Delete only the state.db file + // Do not delete the crypto database files. sessionPaths.fileDirectory.listFiles().orEmpty() - .filter { it.name.contains("matrix-sdk-state") } + .filterNot { it.name.contains("matrix-sdk-crypto") } .forEach { file -> Timber.w("Deleting file ${file.name}...") file.safeDelete() From b043ec8796977a41d7bf8541babecff69547061b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 Sep 2024 12:11:53 +0200 Subject: [PATCH 47/57] Remove subtitle. --- .../securebackup/impl/reset/root/ResetIdentityRootView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootView.kt index 9983f06b44..af7676c93c 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootView.kt @@ -54,7 +54,6 @@ fun ResetIdentityRootView( modifier = modifier, iconStyle = BigIcon.Style.AlertSolid, title = stringResource(R.string.screen_encryption_reset_title), - subTitle = stringResource(R.string.screen_encryption_reset_subtitle), isScrollable = true, content = { Content() }, buttons = { From 17ca03d76bb76519e277dcbbde91b95419384d76 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 2 Sep 2024 10:19:03 +0000 Subject: [PATCH 48/57] Update screenshots --- ...ebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png | 4 ++-- ...ebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png | 4 ++-- ...ackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png | 4 ++-- ...ackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png index 90ecf6e671..b88e01dba2 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45293f2b31c12ae11e2972e502618bad0a2cd78957ac0d80780ed43dff3b5727 -size 79919 +oid sha256:5c2ad0bf991ad9b38b9590a938c63632202d708bde778ef109d9f67ade3cec77 +size 65566 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png index 7c39ece838..fa8578b783 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abf660fec7420f3f648e96fed744458f2d38d29fe09883ad4d2c4956710c549c -size 61943 +oid sha256:c31611d1cd4f738636653fc14ab4a58b6774f7f9a9bc47d6842f4ab2f1f71538 +size 57863 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png index 002e8e86a7..a82f418713 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be591f829e9deed65671eb9273ffa4319c0fcc6d7c155a83fdc4b8b0dee7048f -size 78322 +oid sha256:30985182d2e5d6d0c2eba16daf31d7ac45f4b5dcba549e60a7449f13f60abf80 +size 64348 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png index 910619aaaf..3116acb351 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:772beb3d1591eddfcca833a0f6b79bfda34c0b1bf9c3fca3808ded994353addb -size 59932 +oid sha256:4e4416be0cc1ca9fd304e5c2574b7c0205da667f9b398a3fff2f3ecb6436682f +size 55779 From 1cc87cfed1d15aa16396aeda3697b3a48f9b0476 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 Sep 2024 13:19:25 +0200 Subject: [PATCH 49/57] Fix typo (also fixed on Localazy). --- .../securebackup/impl/src/main/res/values-fr/translations.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/securebackup/impl/src/main/res/values-fr/translations.xml b/features/securebackup/impl/src/main/res/values-fr/translations.xml index 9b1276f654..6656e0a8e3 100644 --- a/features/securebackup/impl/src/main/res/values-fr/translations.xml +++ b/features/securebackup/impl/src/main/res/values-fr/translations.xml @@ -59,6 +59,6 @@ "Cette opération ne peut pas être annulée." "Une erreur s’est produite. Vérifiez que le mot de passe de votre compte est correct et réessayez." "Saisissez…" - "Veuillez confirmec que vous souhaitez réinitialiser votre identité." + "Veuillez confirmer que vous souhaitez réinitialiser votre identité." "Saisissez le mot de passe de votre compte pour continuer" From 94f138840273b6a98ceb935b940c87dd9af4c13a Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 2 Sep 2024 17:14:54 +0200 Subject: [PATCH 50/57] Use the right colors for `@room` mention pills (#3376) * `@room` mentions the current user, so it should use the same colors as an explicit mention to them * Update screenshots --------- Co-authored-by: ElementBot --- .../libraries/textcomposer/mentions/MentionSpan.kt | 4 ++-- .../libraries/textcomposer/mentions/MentionSpanTheme.kt | 9 +++++++-- ...s.textcomposer.mentions_MentionSpanTheme_Day_0_en.png | 4 ++-- ...textcomposer.mentions_MentionSpanTheme_Night_0_en.png | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt index a095320e0a..78f66d5220 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt @@ -61,12 +61,12 @@ class MentionSpan( backgroundColor = when (type) { Type.USER -> if (isCurrentUser) mentionSpanTheme.currentUserBackgroundColor else mentionSpanTheme.otherBackgroundColor Type.ROOM -> mentionSpanTheme.otherBackgroundColor - Type.EVERYONE -> mentionSpanTheme.otherBackgroundColor + Type.EVERYONE -> mentionSpanTheme.currentUserBackgroundColor } textColor = when (type) { Type.USER -> if (isCurrentUser) mentionSpanTheme.currentUserTextColor else mentionSpanTheme.otherTextColor Type.ROOM -> mentionSpanTheme.otherTextColor - Type.EVERYONE -> mentionSpanTheme.otherTextColor + Type.EVERYONE -> mentionSpanTheme.currentUserTextColor } backgroundPaint.color = backgroundColor val (startPaddingPx, endPaddingPx) = mentionSpanTheme.paddingValuesPx.value diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanTheme.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanTheme.kt index f3befe34cb..fbbe96fa99 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanTheme.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanTheme.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.textcomposer.mentions import android.graphics.Color import android.graphics.Typeface +import android.net.Uri import android.text.Spanned import android.view.ViewGroup import android.widget.TextView @@ -129,6 +130,7 @@ val LocalMentionSpanTheme = staticCompositionLocalOf { eventId = null, viaParameters = persistentListOf(), ) + "@room" -> PermalinkData.FallbackLink(Uri.EMPTY, false) else -> throw AssertionError("Unexpected value $uriString") } } @@ -139,7 +141,8 @@ val LocalMentionSpanTheme = staticCompositionLocalOf { val textColor = ElementTheme.colors.textPrimary.toArgb() fun mentionSpanMe() = provider.getMentionSpanFor("mention", "https://matrix.to/#/@me:matrix.org") fun mentionSpanOther() = provider.getMentionSpanFor("mention", "https://matrix.to/#/@other:matrix.org") - fun mentionSpanRoom() = provider.getMentionSpanFor("room", "https://matrix.to/#/#room:matrix.org") + fun mentionSpanRoom() = provider.getMentionSpanFor("room:matrix.org", "https://matrix.to/#/#room:matrix.org") + fun mentionSpanEveryone() = provider.getMentionSpanFor("@room", "@room") mentionSpanTheme.updateStyles(currentUserId = UserId("@me:matrix.org")) CompositionLocalProvider( @@ -154,7 +157,9 @@ val LocalMentionSpanTheme = staticCompositionLocalOf { append("@mention", mentionSpanMe(), 0) append(" to the current user and this is a ") append("@mention", mentionSpanOther(), 0) - append(" to other user. This one is for a room: ") + append(" to other user. This is for everyone in the ") + append("@room", mentionSpanEveryone(), 0) + append(". This one is for a link to another room: ") append("#room:matrix.org", mentionSpanRoom(), 0) append("\n\n") append("This ") diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en.png index 58a71dd0cd..5f6cb5caad 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:489ef02595ca267fa827c2cb2a66c609ec166efa2e047896392613195b5a1e1b -size 36967 +oid sha256:f541f0a05b49272ef3e379a9a629c71458b37435928338a3a58ac852b38eb803 +size 48802 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en.png index d42c2b1372..81f4997ffc 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24c72884be8a2e98f0e5c5cb3911bb1121c75c8b7641062c66f60cded6477c99 -size 35107 +oid sha256:a542b3be557dddc3aba16bda8bb7db1bf0078e6bb3abcc42a11c723e8dba3504 +size 46683 From 5419fa003a55f999038e4c059d9f2f6fae49d6b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:17:25 +0000 Subject: [PATCH 51/57] Update mobile-dev-inc/action-maestro-cloud action to v1.9.1 --- .github/workflows/maestro.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index fd2cc75fd0..a2693dbd2a 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -79,7 +79,7 @@ jobs: uses: actions/download-artifact@v4 with: name: elementx-apk-maestro - - uses: mobile-dev-inc/action-maestro-cloud@v1.8.1 + - uses: mobile-dev-inc/action-maestro-cloud@v1.9.1 if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} From 965e445d04f783073108d33f9d3ba8978a3ed755 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 2 Sep 2024 20:02:06 +0200 Subject: [PATCH 52/57] Replace OSS licenses plugin with Licensee and some manually done UI. This should fix both configuration cache and reproducible F-droid builds. Cleanup and remove gplay/fdroid diff on open source licenses. Co-authored by @jmartinesp --- app/build.gradle.kts | 67 ++++++++-- app/src/gplay/AndroidManifest.xml | 12 -- .../OssOpenSourcesLicensesProvider.kt | 37 ------ app/src/gplay/res/values-night/colors.xml | 32 ----- app/src/gplay/res/values-v27/themes.xml | 25 ---- app/src/gplay/res/values/colors.xml | 32 ----- app/src/gplay/res/values/styles.xml | 23 ---- app/src/gplay/res/values/themes.xml | 41 ------ build.gradle.kts | 22 ---- features/licenses/api/build.gradle.kts | 28 +++++ .../api/OpenSourceLicensesEntryPoint.kt} | 10 +- features/licenses/impl/build.gradle.kts | 49 ++++++++ .../DefaultOpenSourcesLicensesEntryPoint.kt | 16 +-- .../licenses/impl/DependenciesFlowNode.kt | 79 ++++++++++++ .../licenses/impl/LicensesProvider.kt | 53 ++++++++ .../impl/details/DependenciesDetailsNode.kt | 54 ++++++++ .../impl/details/DependenciesDetailsView.kt | 90 +++++++++++++ .../impl/list/DependencyLicensesListNode.kt | 58 +++++++++ .../list/DependencyLicensesListPresenter.kt | 50 ++++++++ .../impl/list/DependencyLicensesListState.kt} | 15 ++- .../DependencyLicensesListStateProvider.kt | 61 +++++++++ .../impl/list/DependencyLicensesListView.kt | 118 ++++++++++++++++++ .../impl/model/DependencyLicenseItem.kt | 53 ++++++++ .../DependencyLicensesListPresenterTest.kt | 71 +++++++++++ .../impl/list/FakeLicensesProvider.kt | 29 +++++ features/preferences/impl/build.gradle.kts | 1 + .../preferences/impl/PreferencesFlowNode.kt | 15 ++- .../preferences/impl/about/AboutNode.kt | 8 +- .../preferences/impl/about/AboutPresenter.kt | 6 +- .../preferences/impl/about/AboutState.kt | 1 - .../impl/about/AboutStateProvider.kt | 6 +- .../preferences/impl/about/AboutView.kt | 10 +- .../impl/about/AboutPresenterTest.kt | 15 +-- .../preferences/impl/about/AboutViewTest.kt | 16 +-- gradle/libs.versions.toml | 3 +- .../main/kotlin/extension/AssetCopyTask.kt | 55 ++++++++ plugins/src/main/kotlin/extension/Utils.kt | 30 ++++- .../tests/konsist/KonsistClassNameTest.kt | 1 + 38 files changed, 983 insertions(+), 309 deletions(-) delete mode 100644 app/src/gplay/AndroidManifest.xml delete mode 100644 app/src/gplay/kotlin/io/element/android/x/licenses/OssOpenSourcesLicensesProvider.kt delete mode 100644 app/src/gplay/res/values-night/colors.xml delete mode 100644 app/src/gplay/res/values-v27/themes.xml delete mode 100644 app/src/gplay/res/values/colors.xml delete mode 100644 app/src/gplay/res/values/styles.xml delete mode 100644 app/src/gplay/res/values/themes.xml create mode 100644 features/licenses/api/build.gradle.kts rename features/{preferences/api/src/main/kotlin/io/element/android/features/preferences/api/OpenSourceLicensesProvider.kt => licenses/api/src/main/kotlin/io/element/android/features/licenses/api/OpenSourceLicensesEntryPoint.kt} (71%) create mode 100644 features/licenses/impl/build.gradle.kts rename app/src/fdroid/kotlin/io/element/android/x/licenses/FdroidOpenSourceLicensesProvider.kt => features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DefaultOpenSourcesLicensesEntryPoint.kt (60%) create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/LicensesProvider.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsNode.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/details/DependenciesDetailsView.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenter.kt rename features/{preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/FakeOpenSourceLicensesProvider.kt => licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListState.kt} (62%) create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListStateProvider.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListView.kt create mode 100644 features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/model/DependencyLicenseItem.kt create mode 100644 features/licenses/impl/src/test/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListPresenterTest.kt create mode 100644 features/licenses/impl/src/test/kotlin/io/element/android/features/licenses/impl/list/FakeLicensesProvider.kt create mode 100644 plugins/src/main/kotlin/extension/AssetCopyTask.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7778a3246e..49641a16cd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,15 +17,19 @@ @file:Suppress("UnstableApiUsage") import com.android.build.api.variant.FilterConfiguration.FilterType.ABI +import com.android.build.gradle.internal.tasks.factory.dependsOn +import com.android.build.gradle.tasks.GenerateBuildConfig +import extension.AssetCopyTask +import extension.GitBranchNameValueSource +import extension.GitRevisionValueSource import extension.allEnterpriseImpl import extension.allFeaturesImpl import extension.allLibrariesImpl import extension.allServicesImpl -import extension.gitBranchName -import extension.gitRevision import extension.koverDependencies import extension.locales import extension.setupKover +import java.util.Locale plugins { id("io.element.android-compose-application") @@ -36,7 +40,8 @@ plugins { id(libs.plugins.firebaseAppDistribution.get().pluginId) alias(libs.plugins.knit) id("kotlin-parcelize") - id("com.google.android.gms.oss-licenses-plugin") + alias(libs.plugins.licensee) + alias(libs.plugins.kotlin.serialization) // To be able to update the firebase.xml files, uncomment and build the project // id("com.google.gms.google-services") } @@ -61,9 +66,6 @@ android { abiFilters += listOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64") } - buildConfigField("String", "GIT_REVISION", "\"${gitRevision()}\"") - buildConfigField("String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\"") - // Ref: https://developer.android.com/studio/build/configure-apk-splits.html#configure-abi-split splits { // Configures multiple APKs based on ABI. @@ -215,6 +217,9 @@ androidComponents { output.versionCode.set((output.versionCode.orNull ?: 0) * 10 + abiCode) } } + + val reportingExtension: ReportingExtension = project.extensions.getByType(ReportingExtension::class.java) + configureLicensesTasks(reportingExtension) } // Knit @@ -259,8 +264,6 @@ dependencies { // Comment to not include unified push in the project implementation(projects.libraries.pushproviders.unifiedpush) - "gplayImplementation"(libs.play.services.oss.licenses) - implementation(libs.appyx.core) implementation(libs.androidx.splash) implementation(libs.androidx.core) @@ -291,3 +294,51 @@ dependencies { koverDependencies() } + +tasks.withType().configureEach { + outputs.upToDateWhen { false } + val gitRevision = providers.of(GitRevisionValueSource::class.java) {}.get() + val gitBranchName = providers.of(GitBranchNameValueSource::class.java) {}.get() + android.defaultConfig.buildConfigField("String", "GIT_REVISION", "\"$gitRevision\"") + android.defaultConfig.buildConfigField("String", "GIT_BRANCH_NAME", "\"$gitBranchName\"") +} + +licensee { + allow("Apache-2.0") + allow("MIT") + allow("GPL-2.0-with-classpath-exception") + allow("BSD-2-Clause") + allowUrl("https://opensource.org/licenses/MIT") + allowUrl("https://developer.android.com/studio/terms.html") + allowUrl("http://openjdk.java.net/legal/gplv2+ce.html") + allowUrl("https://www.zetetic.net/sqlcipher/license/") + allowUrl("https://jsoup.org/license") + allowUrl("https://asm.ow2.io/license.html") + ignoreDependencies("com.github.matrix-org", "matrix-analytics-events") +} + +fun Project.configureLicensesTasks(reportingExtension: ReportingExtension) { + androidComponents { + onVariants { variant -> + val capitalizedVariantName = variant.name.replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase(Locale.getDefault()) + } else { + it.toString() + } + } + val artifactsFile = reportingExtension.file("licensee/android$capitalizedVariantName/artifacts.json") + + val copyArtifactsTask = + project.tasks.register("copy${capitalizedVariantName}LicenseeReportToAssets") { + inputFile.set(artifactsFile) + targetFileName.set("licensee-artifacts.json") + } + variant.sources.assets?.addGeneratedSourceDirectory( + copyArtifactsTask, + AssetCopyTask::outputDirectory, + ) + copyArtifactsTask.dependsOn("licenseeAndroid$capitalizedVariantName") + } + } +} diff --git a/app/src/gplay/AndroidManifest.xml b/app/src/gplay/AndroidManifest.xml deleted file mode 100644 index 234003d953..0000000000 --- a/app/src/gplay/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/app/src/gplay/kotlin/io/element/android/x/licenses/OssOpenSourcesLicensesProvider.kt b/app/src/gplay/kotlin/io/element/android/x/licenses/OssOpenSourcesLicensesProvider.kt deleted file mode 100644 index 93848c438d..0000000000 --- a/app/src/gplay/kotlin/io/element/android/x/licenses/OssOpenSourcesLicensesProvider.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.x.licenses - -import android.app.Activity -import android.content.Intent -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity -import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.features.preferences.api.OpenSourceLicensesProvider -import io.element.android.libraries.di.AppScope -import io.element.android.libraries.ui.strings.CommonStrings -import javax.inject.Inject - -@ContributesBinding(AppScope::class) -class OssOpenSourcesLicensesProvider @Inject constructor() : OpenSourceLicensesProvider { - override val hasOpenSourceLicenses: Boolean = true - - override fun navigateToOpenSourceLicenses(activity: Activity) { - val title = activity.getString(CommonStrings.common_open_source_licenses) - OssLicensesMenuActivity.setActivityTitle(title) - activity.startActivity(Intent(activity, OssLicensesMenuActivity::class.java)) - } -} diff --git a/app/src/gplay/res/values-night/colors.xml b/app/src/gplay/res/values-night/colors.xml deleted file mode 100644 index e830bc1cb1..0000000000 --- a/app/src/gplay/res/values-night/colors.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - #FF101317 - - #FFEBEEF2 - - #ff808994 - - #FF4187EB - - false - false - - diff --git a/app/src/gplay/res/values-v27/themes.xml b/app/src/gplay/res/values-v27/themes.xml deleted file mode 100644 index 99fe605d0a..0000000000 --- a/app/src/gplay/res/values-v27/themes.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - diff --git a/app/src/gplay/res/values/themes.xml b/app/src/gplay/res/values/themes.xml deleted file mode 100644 index 95f0429e0d..0000000000 --- a/app/src/gplay/res/values/themes.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - -